[BUG] Environment variables expand to empty strings when used in piped commands

Resolved 💬 2 comments Opened Jan 9, 2026 by xwang233 Closed Jan 9, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Environment variables expand to empty strings when commands contain pipes in the Bash tool.

What Should Happen?

Environment variables should be correctly expanded in piped commands, just as they are in non-piped commands.

Error Messages/Logs

# Debug output shows variable already empty before execution:
set -x; echo "Value: ${SECRET_TOKEN}" | cat; set +x
+(eval):1> echo 'Value: '  # Variable expanded to empty
+(eval):1> cat

Steps to Reproduce

  1. Set an environment variable: export SECRET_TOKEN="abc123"
  2. Test without pipe: echo "Token: ${SECRET_TOKEN}" → Works: "Token: abc123"
  3. Test with pipe: echo "Token: ${SECRET_TOKEN}" | cat → Fails: "Token: "

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.2 (Claude Code)

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

  • This is a bug in Claude Code's Bash tool, not the user's shell environment
  • Bug confirmed in both bash and zsh environments
  • When instructing Claude Code to use the Bash tool:
  • echo "${SECRET_TOKEN}" → Works
  • echo "${SECRET_TOKEN}" | cat → Fails (empty string)
  • bash -c 'echo "${SECRET_TOKEN}" | cat' → Works
  • Root cause: Bash tool executes via bash/zsh -c -l source snapshot.sh && eval 'command', variable expansion happens at wrong parsing stage when pipes are present
! echo $TEST_SECRET
  ⎿  mysecret123

❯ Can you run these commands using your Bash tool and tell me the results?

  1. Without pipe:
  echo "Value: ${TEST_SECRET}"

  2. With pipe:
  echo "Value: ${TEST_SECRET}" | cat

  3. With bash -c wrapper:
  bash -c 'echo "Value: ${TEST_SECRET}" | cat'

  Also, can you show me what shell process is being used?
  ps -p $$ -o cmd=

● I'll run these commands for you using the Bash tool to show the different behaviors.

● Bash(echo "Value: ${TEST_SECRET}")
  ⎿  Value: mysecret123

● Bash(echo "Value: ${TEST_SECRET}" | cat)
  ⎿  Value:

● Bash(bash -c 'echo "Value: ${TEST_SECRET}" | cat')
  ⎿  Value: mysecret123

● Bash(ps -p $$ -o cmd=)
  ⎿ /bin/bash -c -l source /root/.claude/shell-snapshots/snapshot-bash-1767989188783-j4g8gy.sh && shopt -u extglob 2>/dev/null || true && eval 'ps -p $$ -o cmd=' \< /dev/null && pwd -P >| /tmp/claude-c590
    -cwd


● Here are the results:

  1. Without pipe:
  Value: mysecret123
  ✓ The environment variable is expanded correctly.

  2. With pipe:
  Value:
  ✗ The environment variable is NOT expanded when piped through cat.

  3. With bash -c wrapper:
  Value: mysecret123
  ✓ Using bash -c preserves the environment variable expansion even with the pipe.

  Shell process:
  /bin/bash -c -l source /root/.claude/shell-snapshots/snapshot-bash-1767989188783-j4g8gy.sh && shopt -u extglob 2>/dev/null || true && eval 'ps -p $$ -o cmd=' < /dev/null && pwd -P >| /tmp/claude-c590-cwd

  The shell is /bin/bash. The interesting behavior you're seeing is that when using pipes directly, the environment variable expansion appears to fail, but wrapping the entire command in bash -c preserves it. This is likely due to how the command execution environment handles variable expansion in different contexts.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗