Do Stack Traces and Tokens Belong on the Clipboard?
A stack trace is supposed to be safe to share. It is the call stack of a failing program, with file names and line numbers, intended to help a maintainer diagnose the failure. In practice, modern stack traces are not safe to share by default. They frequently capture tokens, request IDs, connection strings, bearer headers, and occasionally the full request body that triggered the failure. Pasting a raw stack trace into Slack, a Jira ticket, or a public GitHub issue is one of the most common ways credentials leak from engineering teams.
This guide is about the intersection of stack traces, secrets, and the clipboard. It is written for developers, SREs, and support engineers who regularly paste stack traces into tickets and chat. For neighbouring topics, see copying error logs without taking half the console, best clipboard habits for developers in 2026, and API keys on the clipboard are an incident.
Where tokens hide in a stack trace
Tokens in stack traces come from four common sources. None of them are obvious without a habit of looking.
1. Function arguments printed by the runtime
Some runtimes print function arguments alongside the stack frame. Java's Arrays.toString on a byte array, JavaScript's console.log of an Error object, and Python's repr of exception arguments can all surface the original arguments to the failing call. If one of those arguments was a token — for example, verifyToken("eyJhbGciOi...") — the token is now in the stack trace.
2. Request context attached by middleware
Many web frameworks attach the current request to the exception or the logging context. Flask's g, Express's req, Django's request, and ASP.NET's HttpContext.Current all do this in different ways. If the framework's error printer dumps the request, the stack trace will include headers, cookies, query parameters, and the request body — any of which may contain tokens.
3. Connection strings in init errors
Database connection errors frequently print the connection string. psycopg2.OperationalError: could not connect to server: postgres://user:s3cretP@ss@db-prod-01:5432/app is a real connection string with a real password, in a stack trace that looks like any other stack trace. The same applies to Redis, MongoDB, and any service that uses URI-style connection strings.
4. Environment variable dumps on misconfiguration
When a required environment variable is missing, some libraries dump the entire environment block to help with debugging. This is rare in production-grade libraries but common in internal tooling and in libraries that were not designed for production. A dump of process.env or os.environ will include every secret the application has access to.
What to redact, by pattern
Before pasting a stack trace, scan for and replace these patterns:
| Pattern | Regex (rough) | Replacement | ||
|---|---|---|---|---|
| Bearer token | Bearer\s+[A-Za-z0-9_\-.]+ | Bearer <redacted> | ||
| JWT | eyJ[A-Za-z0-9_\-.]+ | <jwt redacted> | ||
| AWS access key | AKIA[0-9A-Z]{16} | AKIA<redacted> | ||
| Connection string with password | ://[^:\s]+:[^@\s]+@ | ://user:<redacted>@ | ||
| Private key block | -----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY----- | <private key redacted> | ||
Generic API key (long hex or base64 in a key/token/secret field) | `"(?:api[_-]?key\ | token\ | secret)"\s*:\s*["'][^"']{16,}["']` | "api_key": "<redacted>" |
| Slack token | xox[baprs]-[0-9A-Za-z-]+ | <slack token redacted> | ||
| GitHub token | gh[pousr]_[A-Za-z0-9]{36,} | <github token redacted> |
The replacement should communicate "there was a secret here" so the reader knows the value was redacted, not omitted by accident. <redacted> is the conventional placeholder.
A practical redaction workflow
The workflow that works:
- Copy the raw stack trace to a scratch buffer. A text editor (VS Code, Notepad++) is safer than the clipboard because it does not feed into Win+V history or any clipboard manager.
- Run the redaction patterns. For one-off redactions, a few find-and-replace passes in the editor are enough. For repeated redactions, a small script (
sedor a Python one-liner) is faster and more reliable. - Read the result end to end. Regexes miss things — a connection string in an unusual format, a token in an unexpected field. The final read catches what the patterns miss.
- Copy the redacted version to the clipboard and paste. This is the version that goes into Slack or the ticket.
For a team that does this often, a small CLI script is worth writing. Example skeleton:
import re, sys, pyperclip
text = sys.stdin.read()
patterns = [
(r'Bearer\s+[A-Za-z0-9_\-.]+', 'Bearer <redacted>'),
(r'eyJ[A-Za-z0-9_\-.]+', '<jwt redacted>'),
(r'AKIA[0-9A-Z]{16}', 'AKIA<redacted>'),
(r'://[^:\s]+:[^@\s]+@', '://user:<redacted>@'),
(r'xox[baprs]-[0-9A-Za-z-]+', '<slack token redacted>'),
(r'gh[pousr]_[A-Za-z0-9]{36,}', '<github token redacted>'),
]
for pat, rep in patterns:
text = re.sub(pat, rep, text)
pyperclip.copy(text)
print("Redacted and copied to clipboard.")
This reads from stdin, applies the patterns, and copies the redacted text to the clipboard. program 2>&1 | python redact.py becomes a routine part of the debug workflow. For teams that want more comprehensive coverage, the detect-secrets tool from Yelp and the gitleaks tool both ship with broader pattern libraries.
What if a token already reached the clipboard
If a token reached the clipboard — through a careless copy, an auto-captured stack trace, or a misbehaving logging library — the response is the same as any credential exposure:
- Rotate the token. This is the only safe response. Treating "clearing the clipboard" as enough is a mistake; the token may already be in Win+V history (if enabled), in a clipboard manager's history (if running), in cloud sync (if enabled), and in any chat or ticket it was pasted into.
- Clear clipboard history. Win+V → Clear all, or the equivalent in the clipboard manager. This does not undo the exposure but reduces the window.
- Audit where the token went. Search Slack, Jira, GitHub, and email for the token's prefix (first 6-8 characters). If it is found, redact or delete the message and notify anyone who saw it.
- Review the workflow that produced the exposure. Stack traces should not capture tokens by default. If they do, the logging library or the framework's error printer needs configuration to redact or omit sensitive fields.
For deeper coverage of rotation and incident response, see API keys on the clipboard are an incident. The same rotation rule applies to env files: the .env file should never touch history.
Pasting into Slack versus a ticket
Slack and Jira have different exposure profiles, and the redaction strategy differs.
Slack — messages are searchable across the workspace by anyone in the workspace, and Slack's retention settings determine how long the message persists. A token pasted into a Slack channel is reachable by every workspace member until the message is deleted (and even then, search indices may lag). Treat Slack as a public channel for redaction purposes, even for private channels.
Jira — tickets are visible to anyone with project access, and ticket history is preserved indefinitely. A token pasted into a Jira ticket is in the ticket's history forever, even if the comment is deleted. Treat Jira as a permanent record for redaction purposes.
GitHub issues — public repos are indexed by search engines. A token pasted into a public GitHub issue is on the public internet within minutes and will be scraped by automated token-scanning bots (GitHub's own secret scanning, GitGuardian, and others). GitHub will typically auto-revoke known patterns (AWS, Slack, GitHub) and notify the owner, but the token should still be rotated. For private repos, the exposure is smaller but the same rotation discipline applies.
Preventing the next exposure
Redaction at paste time is reactive. The proactive version is preventing the token from entering the stack trace in the first place. Three layers help:
- Configure the logging library to redact known sensitive fields. Most modern logging libraries (Python's
structlog, Java's Logback with a custom encoder, Node'spinowith a redaction plugin) support a list of field names whose values are replaced with<redacted>before the log line is written. This catchesauthorization,password,api_key,secret,token, and similar fields automatically. - Use a secrets manager instead of environment variables where possible. Environment variables are easy to dump accidentally; secrets managers (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) are designed to be retrieved at runtime without being printable. The trade-off is operational complexity.
- Run a secret scanner in CI. Tools like
detect-secretsorgitleakscan be wired into pre-commit hooks and CI pipelines to catch tokens before they reach a repo. This does not catch tokens pasted into Slack or Jira, but it catches the subset that ends up in code or documentation.
None of these are a substitute for the redaction habit at paste time. They are complementary layers that reduce the frequency and severity of exposure.
What a good redacted stack trace looks like
For a real example, consider a Python traceback from a Flask app where the request object was attached to the exception:
Before redaction:
Traceback (most recent call last):
File "src/auth/handlers.py", line 42, in login
user = verify_token(request.headers["Authorization"])
File "src/auth/tokens.py", line 18, in verify_token
raise InvalidTokenError("expired")
auth.InvalidTokenError: expired
Request: POST /api/login
Headers: {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW1wbGUuY29tIiwiaWF0IjoxNzI0MDY3MDIwLCJleHAiOjE3MjQwNjczMjB9.invalidsignature",
"User-Agent": "curl/8.0.1",
"X-Forwarded-For": "10.0.0.42"
}
After redaction:
Traceback (most recent call last):
File "src/auth/handlers.py", line 42, in login
user = verify_token(request.headers["Authorization"])
File "src/auth/tokens.py", line 18, in verify_token
raise InvalidTokenError("expired")
auth.InvalidTokenError: expired
Request: POST /api/login
Headers: {
"Authorization": "Bearer <redacted>",
"User-Agent": "curl/8.0.1",
"X-Forwarded-For": "10.0.0.42"
}
The redacted version communicates the same failure: a token verification failed because the token was expired. The maintainer can diagnose the bug from this. The original token — which would have allowed a determined attacker to forge requests for the remaining seconds of its validity — is gone. This is the discipline. It takes 15 seconds with a find-and-replace; it takes 15 minutes to rotate the token and audit where it went.
Related reading
- VS Code: Built-In Clipboard Ring vs an OS Manager
- JetBrains Copy/Paste History vs a System Shelf
- Best Clipboard Habits for Developers in 2026
- Clipboard Tools on Multi-Monitor Windows Setups
Sources
- OWASP — Sensitive data exposure prevention cheat sheet — reference for what counts as sensitive data and how to handle it across the application lifecycle
- Yelp — detect-secrets tool — open-source secret scanner with a broad pattern library; can be integrated into pre-commit hooks to catch secrets before they reach a repo
- Gitleaks — secret scanning tool — open-source secret scanner for git history; useful for auditing existing repos for past leaks
- GitHub — Secret scanning documentation — official GitHub documentation on how secret scanning detects and revokes exposed tokens in public repos
- Slack — Information security and data retention — Slack's official documentation on data retention, which affects how long a pasted token remains reachable
Deepender Yadav is a B.Tech Computer Science Engineering student and software developer interested in building practical software and open-source projects.
GitHub · LinkedInCopy. Stack. Drop.
Transform your clipboard into an interactive edge shelf. Stack, pin, and drag assets into any app with zero friction.
Download for Windows Get from Microsoft Store
How to Install Guide · First 10 Minutes Guide · Drag & Drop Guide · Edge-Drop vs Win+V · Support
Free · Lightweight · Privacy First