Shell variable expansion fails in piped commands ($VAR expands to empty with |)

Resolved 💬 3 comments Opened Mar 12, 2026 by JasonBroderick Closed Mar 16, 2026

Description

Shell variable expansion ($VAR) produces empty strings when the Bash tool command contains a pipe (|). The same variable expands correctly without a pipe. This affects all environment variables, not just specific ones.

Reproduction

All of these can be run directly in Claude Code's Bash tool:

# Works (no pipe):
echo "$HOME"
# → /home/jason

# Fails (with pipe):
echo "$HOME" | cat
# → (empty)

# Fails (with pipe):
echo "$HOME" | wc -c
# → 1   (just the newline — $HOME expanded to empty)

# Works (no pipe):
printf "%s" "$HOME"
# → /home/jason

# Fails (with pipe):
printf "%s" "$HOME" | cat
# → (empty)

# Works (redirect instead of pipe):
echo "$HOME" > /tmp/test.txt && cat /tmp/test.txt
# → /home/jason

# Process env IS correct:
printenv HOME | wc -c
# → 12   (printenv reads from process env directly — correct)

Key observations

  • $VAR expands correctly without a pipe
  • $VAR expands to empty string with a pipe
  • printenv VAR (reads process env directly) works correctly even in pipes
  • $(printenv VAR) works as a workaround: echo "$(printenv HOME)" | wc -c12
  • bash -c 'echo "$HOME"' | cat works (single-quoted, so inner bash expands from inherited env)
  • File redirects (>) work correctly — only | pipes are affected
  • declare -p HOME shows declare -x HOME="/home/jason" — the variable IS declared and exported in the shell

Impact

This is a critical issue for AI agent workflows that use curl with auth tokens piped to jq/python3 -m json.tool for parsing:

# This sends an EMPTY Authorization header:
curl -s -H "Authorization: Bearer $API_KEY" "$API_URL/endpoint" | jq .

# This works:
curl -s -H "Authorization: Bearer $API_KEY" "$API_URL/endpoint"

In our case, this caused a Paperclip AI agent orchestration system to have cascading authentication failures — the agent's JWT token expanded to empty in every piped curl command, causing checkout conflicts, wrong author identity, and ownership errors. Non-piped commands from the same session worked perfectly.

Environment

  • Claude Code version: 2.1.58
  • OS: Ubuntu on WSL2 (Linux 6.6.87.1-microsoft-standard-WSL2)
  • Shell: bash 5.2.21
  • The Bash tool uses bash -c "source <snapshot> && eval '<command>' < /dev/null" execution pattern

Workaround

Replace $VAR with $(printenv VAR) in any command that includes a pipe.

Root cause hypothesis

The Bash tool wraps commands via eval 'COMMAND' < /dev/null. When the COMMAND string contains a pipe, variable expansion behaves differently than without a pipe. Direct bash -c testing of the same eval pattern works correctly, so the issue may be in how Claude Code constructs or passes the command string to bash, possibly related to quoting or escaping of the command before it reaches eval.

View original on GitHub ↗

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