Repository navigation
Description
Preflight Checklist
- I have searched existing issues and this hasn't been reported yet
- This is a single bug report (please file separate reports for different bugs)
- I am using the latest version of Claude Code
What's Wrong?
When using the Bash tool to access a custom environment variable, say CLAUDE_AGENT_ID, with a pipe operator, the variable expands to only a newline character instead of its actual value "alpha". The environment variable exists and is visible with env | grep CLAUDE_AGENT_ID, but when piped to another command like wc -c, it loses its value.
What Should Happen?
The CLAUDE_AGENT_ID environment variable should consistently expand to its actual value "alpha" when used in any bash command, including when piped to other commands.
Error Messages/Logs
# Variable exists in environment
$ env | grep CLAUDE_AGENT_ID
CLAUDE_AGENT_ID=alpha
# But expands to only newline when piped
$ echo "$CLAUDE_AGENT_ID" | hexdump -C
00000000 0a |.|
00000001
# Character count shows 1 instead of expected 6
$ echo $CLAUDE_AGENT_ID | wc -c
1Steps to Reproduce
Please provide clear, numbered steps that anyone can follow to reproduce the issue. Important: Include any necessary code, file contents, or context needed to reproduce the bug. If the issue involves specific files or code, please create a minimal example.
-
In Claude Code, use the Bash tool to echo the CLAUDE_AGENT_ID:
echo $CLAUDE_AGENT_ID
Result: Shows "alpha" (appears correct)
-
Use the Bash tool to count characters with a pipe:
echo $CLAUDE_AGENT_ID | wc -c
Result: Returns
1(incorrect - should be 6) -
Verify the variable exists:
env | grep CLAUDE_AGENT_IDResult: Shows
CLAUDE_AGENT_ID=alpha(correct) -
Check actual bytes being output:
echo "$CLAUDE_AGENT_ID" | hexdump -C
Result: Shows only
0a(newline character) -
Run in a new bash subprocess:
bash -c 'echo "$CLAUDE_AGENT_ID" | wc -c'Result: Returns
6(correct - works in subprocess)
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
No response
Claude Code Version
1.0.128 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
WSL (Windows Subsystem for Linux)
Additional Information
Anything else that might help us understand the issue?
- The bug only occurs when using pipes with the environment variable
- Direct echo without pipes appears to work (shows "alpha")
- The variable works correctly when executed in a new bash subprocess using
bash -c - This appears to be a shell state issue where the variable exists in the environment but isn't properly accessible in the current shell context
- Use case where discovered: I observed this issue when trying to inject basic authentication credentials for curl commands using environment variables (e.g.,
curl -u "$USERNAME:$PASSWORD" https://api.example.com). The authentication would fail because the variables weren't expanding correctly when piped or used in command substitution. - Date observed: 2025-09-28
Activity
stockmaj commentedon Nov 21, 2025
Additional Context: Affects More Than Just Pipes
I can confirm this bug and wanted to add that the issue extends beyond just pipe operators - environment variables are also empty when passed to subprocess commands like
curl.Real-World Impact Example
When trying to use an API token in a curl Authorization header:
Result: Authentication fails because curl receives an empty token, even though:
${#MY_API_TOKEN}correctly returns 501 (the token length)env | grep MY_API_TOKENshows the full token valueInconsistency Details
Confirmed Workaround
Writing commands to a script file and executing them works correctly:
This suggests the issue is specifically with how the Bash tool passes environment variables to inline commands vs. script files.
bukzor commentedon Nov 24, 2025
Verified: (the CLAUDE_AGENT_ID doesn't seem to exist anymore, but the same happens with CLAUDE_CODE_ENTRYPOINT)
bukzor commentedon Nov 24, 2025
Workaround: advise Claude to always use bash <<'BASH' .... BASH when using the Bash tool.
smconner commentedon Dec 9, 2025
PreToolUse Hook Workaround for Bash Preprocessing Bugs (v7)
A hook that fixes all known bash preprocessing bugs in Claude Code by wrapping commands in
bash -c '...'.Zero token overhead — hooks run externally before command execution.
v7 Update: "Wrap Everything" Approach
Credit: @AdamScherlis identified a gap in v6's pattern-matching. Commands like
echo $CLAUDE_CODE_ENTRYPOINT | wc -cweren't being wrapped because they don't match any detection patterns (no$(), no newlines, no loop keywords) — yet they still trigger the preprocessing bug.Why Pattern-Matching Failed
v1-v6 tried to detect "problematic" commands by looking for:
$(...)command substitutionfor ... | ...)This was ~30 lines of regex that inevitably had blind spots.
The Fix: Wrap Everything
AdamScherlis's suggestion: just wrap ALL commands unconditionally.
Concerns we evaluated:
bash -cadds microseconds/bin/shvsbashdiffer on some escapes, but Claude usesecho -eor$'...'when it needs escape interpretation — never relies on raw\tin double quotesResult: Simpler code, more robust, future-proof against unknown bugs.
GitHub Issues Fixed
$(...)command substitution mangledInstallation
Step 1: Save the hook
Step 2: Make executable
chmod +x ~/.claude/hooks/fix-bash-substitution.pyStep 3: Configure Claude Code
Run
/hooksin Claude Code, then:~/.claude/hooks/fix-bash-substitution.pyStep 4: Restart session
Start a new Claude Code session for the hook to take effect.
Verifying It Works
Escape Markers
To skip wrapping for a specific command, add a comment:
Changelog
AdamScherlis commentedon Dec 25, 2025
The hook above does not fix this issue, because it doesn't include the "env variable + pipe" failure mode in
needs_wrapping().e.g.
echo $CLAUDE_CODE_ENTRYPOINT | wc -cwill not get wrapped.Skipping the
needs_wrappingcheck and wrapping every bash call seems to work fine; are there downsides to this approach?fix: work around Claude Code bash preprocessing bug
smconner commentedon Dec 25, 2025
@AdamScherlis You're absolutely right — updated the hook to v7 with your "wrap everything" approach. Thank you for the catch and the elegant solution.
The Problem You Identified
echo $CLAUDE_CODE_ENTRYPOINT | wc -cwasn't getting wrapped because v6 looked for specific patterns:$(...)— not presentYet it still triggers the preprocessing bug. Pattern-matching will always have blind spots.
Evaluating "Wrap Everything"
Before adopting your approach, I tested potential downsides:
bash -coverhead/bin/shinterprets\tdifferently thanbash -cecho -eor$'...'for escapes — never relies on the affected behaviorThe only "failures" in the test suite were tests with wrong expectations (relying on
/bin/shquirks that differ from bash).The Fix
~30 lines of pattern detection → 5 lines. Simpler, more robust, future-proof.
Credit added to the hook source and main comment. Thanks again!
github-actions commentedon Jan 24, 2026
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.
Butanium commentedon Feb 1, 2026
this is still happening
mhelvens commentedon Feb 27, 2026
I just reported #29298, which does appear to be a duplicate of this bug. However, this bug report seems like it's being ignored a bit, maybe? So I don't want to close mine as a duplicate yet. It contains a bunch of test cases which may be helpful.
Also, I get the feeling this bug was fixed at some earlier versions and then reintroduced? Either that or I'm delusional.
in-op commentedon Mar 2, 2026
Still an issue on 2.1.63. On mac
brokensandals commentedon Mar 8, 2026
Just ran into this on macos 26.3, claude code 2.1.71, zsh 5.9 (arm64-apple-darwin25.0). It was very time-consuming for me to troubleshoot why the commands that worked in my terminal didn't work for claude; hope this gets fixed at some point!
A simple workaround that seems to be working for me is to tell claude to use
$(printenv VAR)instead of$VAR.hjtran commentedon Mar 11, 2026
There are workarounds for this - but we're spending quite a bit of time implementing them and deploying them widely in our org. It'd really save us a lot of time and pain if a proper fix could be implemented
shawnz-swiftly commentedon Mar 22, 2026
This appears to have been fixed within the last couple of weeks
Butanium commentedon Mar 22, 2026
Indeed!
windows:
linux
claude commentedon Apr 17, 2026
This issue was fixed as of version 2.1.79.
github-actions commentedon Apr 25, 2026
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.