[BUG] `!` bash-command stdout renders `&` as `&` in the transcript — corrupts copyable URLs

Status Open
Reported on v2.1.224
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Aug 8, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Environment

  • Claude Code 2.1.224 (CLI, macOS — Darwin 25.6.0)

Bug

Stdout from a ! bang command is HTML-escaped when rendered in the transcript: every & displays as &. The child process's actual stdout is clean — the same command with stdout redirected to a file shows a literal &.

Repro

! printf 'https://example.com/?a=1&b=2\n'

Transcript shows:

https://example.com/?a=1&b=2

Redirecting the same command to a file and reading the file shows the correct https://example.com/?a=1&b=2.

Impact

Any URL printed for the user to open or copy is silently corrupted — every query parameter after the first is lost (&code_challenge=... reaches the server as a garbage amp;code_challenge parameter).

The common case is OAuth login flows: e.g. railway login auto-opens the browser (which works, since the CLI passes the clean string to the OS) but also prints the authorization URL as a copy-paste fallback. A user who copies the displayed URL — because auto-open failed, the wrong browser profile opened, or they're on a remote box — gets an opaque server-side rejection ("code_challenge_method must be provided"). We spent a month treating this as a vendor CLI bug (with a "never log in from inside Claude Code" workaround) before isolating it to the transcript rendering: the CLI's output was clean all along.

Expected

Bang-command output rendered verbatim, with no HTML entity escaping.

View original on GitHub ↗

3 Comments

bcherny collaborator · 1 month ago

Thanks for the detailed report and the clean isolation to the transcript layer — that clearly took a lot of work.

I attempted to reproduce this on the current release (2.1.233, Linux):

  • Running ! printf 'https://example.com/?a=1&b=2\n' in the interactive transcript displayed the URL correctly (&, not &), both live and after resuming the session. So the display corruption you saw on 2.1.224/macOS did not reproduce on this build — it may have been fixed since, or be specific to another rendering path.
  • However, the escaping does exist under the hood: the stored session transcript records the command's stdout with & encoded as &, and that encoded form is what the model sees as context. In my test session the model itself remarked that the output it received contained & where printf could not have produced it. So a URL copied from model output (rather than from the raw command output block) can still end up corrupted the way you describe.

Could you retest on the latest release and confirm whether the displayed transcript still shows & on macOS? Either way, the ingestion-side escaping is a real defect worth fixing.

🤖 Generated with Claude Code

SanGioSports · 1 month ago

Retested on 2.1.246, macOS (Darwin 25.6.0), same repro ! printf 'https://example.com/?a=1&b=2\n'.

  • Rendered transcript: displays & correctly — the display corruption from 2.1.224 no longer reproduces on macOS either. Confirmed fixed on the display path.
  • Model context: the model received https://example.com/?a=1&b=2 — confirmed by asking it to quote the output verbatim.
  • Stored session JSONL: the <bash-stdout> record holds &amp; (byte-verified with od -c), while the <bash-input> record for the same command holds a literal & — the escaping is applied to stdout on ingestion only, not to the input line.

For reference, the 2.1.224 session behind the original report stored <bash-stdout> the same way (&amp; in every OAuth query separator), so the ingestion defect is unchanged between builds. Agree that's the one worth fixing — any URL the model relays from bang output is still corrupted regardless of what the display shows.

Magical5901 · 27 days ago

Other characters are affected.

! echo "a > b" > /tmp/f (Bash completed with no output) ! cat /tmp/f a > b Reproduced cleanly: the file contains a > b (written by plain echo), but the transcript I receive shows a &gt; b. That's a tighter repro than the git-diff one — two commands, no repo needed.

This is on Amazon Linux 2, Claude Code: 2.1.273.822