[BUG] `!` bash-command stdout renders `&` as `&` in the transcript — corrupts copyable URLs
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.
3 Comments
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):
! 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.&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&whereprintfcould 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
Retested on 2.1.246, macOS (Darwin 25.6.0), same repro
! printf 'https://example.com/?a=1&b=2\n'.&correctly — the display corruption from 2.1.224 no longer reproduces on macOS either. Confirmed fixed on the display path.https://example.com/?a=1&b=2— confirmed by asking it to quote the output verbatim.<bash-stdout>record holds&(byte-verified withod -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 (&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.Other characters are affected.
This is on Amazon Linux 2, Claude Code: 2.1.273.822