Echo vs Printf for CLI Secrets
When piping a value to a CLI tool that stores secrets, `echo` adds a trailing newline that becomes part of the stored value. This silently breaks any credential that's compared byte-for-byte — OAuth client IDs, API tokens, webhook secrets. Always use `printf` instead.
Tags
Echo vs Printf for CLI Secrets
The Lesson
When piping a value to a CLI tool that stores secrets, echo adds a trailing newline that becomes part of the stored value. This silently breaks any credential that's compared byte-for-byte — OAuth client IDs, API tokens, webhook secrets. Always use printf instead.
Context
MyReachBand stores Twilio credentials and Google OAuth keys as Cloudflare Worker secrets via wrangler secret put. The secrets are used in HTTP requests where the exact string must match what the remote service expects. A single invisible character at the end breaks the match.
What Happened
- Set Google OAuth client ID with
echo "790486..." | npx wrangler secret put GOOGLE_CLIENT_ID. - Google OAuth returned
redirect_uri_mismatchon every login attempt. - Verified the redirect URI was correct in the Google console. Verified the Worker code constructed the URL properly.
- Spent significant time debugging the OAuth callback handler, adding try/catch blocks, checking state cookies.
- Eventually realized
echoadds\nto the value. The stored client ID was790486...\n, which got URL-encoded into the redirect URI, which didn't match what Google had registered. - Fixed with
printf "790486..." | npx wrangler secret put GOOGLE_CLIENT_ID. No newline, exact match, OAuth worked immediately.
Key Insights
echoalways adds a newline. On every platform (bash, zsh, PowerShell),echo "value"outputsvalue\n. This is by design — echo is for terminal output, not for piping binary-exact values.printfdoes not add a newline.printf "value"outputs exactlyvalue. Use it whenever the trailing bytes matter.- The error message won't help you. Google says
redirect_uri_mismatch. Twilio saysinvalid credentials. Neither says "your secret has a trailing newline." You have to know to look for it. - Secrets can't be read back.
wrangler secret listshows names, not values. You can't inspect what was stored to verify it's correct. The only test is whether the integration works. - Node.js
process.stdinis safest. For maximum reliability:node -e "process.stdout.write('value')" | npx wrangler secret put NAME. This guarantees no trailing characters regardless of shell.
Examples
Bad
echo "GOCSPX-abc123" | npx wrangler secret put GOOGLE_CLIENT_SECRET
# Stores: "GOCSPX-abc123\n" — broken
Good
printf "GOCSPX-abc123" | npx wrangler secret put GOOGLE_CLIENT_SECRET
# Stores: "GOCSPX-abc123" — correct
Safest (Node.js)
node -e "process.stdout.write('GOCSPX-abc123')" | npx wrangler secret put GOOGLE_CLIENT_SECRET
Implementation Guide
Step 1: Replace echo with printf in all secret-setting commands
Search your scripts, documentation, and shell history for echo piped to secret-setting commands:
# Find echo-piped secrets in scripts and docs
grep -rn "echo.*|.*secret\|echo.*| .*put\|echo.*| .*ssm" scripts/ docs/ *.md
Replace every instance with printf:
# Before (broken — stores "value\n")
echo "$SECRET_VALUE" | npx wrangler secret put MY_SECRET
# After (correct — stores "value")
printf "%s" "$SECRET_VALUE" | npx wrangler secret put MY_SECRET
Use printf "%s" with a format string when the value is in a variable — this prevents printf from interpreting backslash sequences in the value itself.
Step 2: Re-set any secrets that may have been set with echo
Since most secret managers don't let you read back stored values, re-set every secret using printf to be safe:
# Re-set all secrets with printf
printf "%s" "your-client-id" | npx wrangler secret put GOOGLE_CLIENT_ID
printf "%s" "your-client-secret" | npx wrangler secret put GOOGLE_CLIENT_SECRET
printf "%s" "your-api-key" | npx wrangler secret put TWILIO_AUTH_TOKEN
If you're unsure whether a secret was set correctly, the fastest test is to use it — a trailing newline will cause authentication failures, and re-setting with printf is a one-command fix.
Step 3: Add a shell alias or wrapper to prevent recurrence
Add a helper function to your shell profile that makes it impossible to accidentally use echo:
# Add to ~/.bashrc or ~/.zshrc
secret-put() {
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: secret-put NAME VALUE"
return 1
fi
printf "%s" "$2" | npx wrangler secret put "$1"
}
Usage: secret-put GOOGLE_CLIENT_ID "790486..." — always uses printf, never adds a newline.
Applicability
This applies to any CLI that reads secrets from stdin: wrangler secret put, aws ssm put-parameter, vault kv put, kubectl create secret. The newline problem is universal. It does NOT apply when the CLI reads from a file or prompts interactively — those typically trim whitespace.
Related Lessons
- Wrangler CLI — the tool where this bug manifests
- Google OAuth2 — the integration that broke due to this bug