Environment variables stripped from command arguments before shell execution

Resolved 💬 3 comments Opened Jan 4, 2026 by paymog Closed Jan 4, 2026

Summary

Environment variables (e.g. $HA_TOKEN) are stripped from command arguments during preprocessing, but remain available in the environment. This causes commands like curl -H "Authorization: Bearer $TOKEN" to fail silently with an empty token value.

Environment

  • Shell: /opt/homebrew/bin/zsh with zprezto
  • Platform: macOS (darwin 24.0)
  • curl: 8.7.1
  • Token source: $HA_TOKEN set in ~/.secrets, sourced by ~/.zshrc
  • Execution: Commands run via eval (visible in set -x traces as +(eval):1>)

Reproduction

# This fails - token is stripped before eval
curl -s -H "Authorization: Bearer $HA_TOKEN" https://example.com/api

# Debug trace shows empty token:
set -x; curl -s -H "Authorization: Bearer $HA_TOKEN" https://example.com/api
# +(eval):1> curl -s -H 'Authorization: Bearer ' https://example.com/api

# But the variable exists and is exported:
printenv | grep HA_TOKEN  # ✓ shows the token
export | grep HA_TOKEN    # ✓ shows the token
echo $HA_TOKEN            # ✓ works (shell builtin)

# External programs CAN access it via environment:
python3 -c "import os; print(os.environ['HA_TOKEN'])"  # ✓ works
node -e "console.log(process.env.HA_TOKEN)"            # ✓ works
sh -c 'echo $HA_TOKEN'    # ✓ works (single quotes defer expansion)

Expected Behavior

Variable should expand normally in command arguments, consistent with shell builtins like echo.

Actual Behavior

Variable is stripped from arguments to external commands (curl, printf, etc.) but remains in the environment. The stripping happens before eval, as evidenced by the debug traces showing an empty value.

Workaround

Wrap commands in a subshell with single quotes so expansion happens in the subshell rather than during preprocessing:

sh -c 'curl -s -H "Authorization: Bearer $HA_TOKEN" https://example.com/api'

Scope

Affected:

  • Any $VAR reference in arguments passed to external commands

Not affected:

  • Shell builtins (echo, export)
  • Variables inside single-quoted strings passed to subshells
  • Direct environment access from within programs (os.environ, process.env)

View original on GitHub ↗

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