[BUG] Fullscreen TUI: failed Bash tool calls drop the entire ⎿ result row (stdout+stderr present in JSONL and tool_result; success path OK)
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?
In fullscreen TUI (/tui → Current renderer: fullscreen), when a Bash tool call exits non-zero, the live UI shows only the command header:
⏺ Bash(echo "STDOUT-MARKER-ABC" && echo "STDERR-MARKER-XYZ" >&2 && exit 1)
There is no ⎿ body line at all — not the captured stdout, not the stderr, not even the bare word Error / Error: Exit code 1. Successful Bash calls in the same session (e.g. echo "hello") render their ⎿ body correctly.
This is a pure display-layer bug:
| Layer | Status |
|-------|--------|
| Command execution | ✅ runs (exit 1 as expected) |
| stdout + stderr capture | ✅ both streams present |
| Session JSONL tool_result | ✅ full content + is_error: true |
| Model-visible tool_result | ✅ model receives and acts on the output |
| Fullscreen TUI render | ❌ drops the entire ⎿ row |
Conditional on failure only. Non-zero exit → missing ⎿. Exit 0 → body shown.
Conditional on fullscreen renderer only. Classic/default TUI (/tui default) correctly shows:
⏺ Bash(echo "This command fails" && exit 1)
⎿ Error: Exit code 1
This command fails
Why this is not just a “me too” on #64474
#64474 (open, 2026-06-01) reports that expanding a failed Bash call shows the command but no output. Our observed failure is worse and more user-hostile:
- The
⎿result row is completely absent — not “empty when expanded,” but never rendered. - There is nothing to expand; the tool call collapses to the header line alone.
- We prove the data path is intact (JSONL + model tool_result below), so this cannot be blamed on capture, sandbox, or empty-command output.
- First confirmed here on 2.1.170 (after #64474’s original 2.1.159 repro) and still broken on 2.1.185 — if anything the render path got worse, not better.
Closest prior art is #64474 (open; “empty when expanded”). Filing separately because our repro is stronger (entire ⎿ row missing), we have end-to-end harness/JSONL/model proof, and we document a hook-based workaround. Not a duplicate of the ENOSPC cluster (#65880 / #63909) or the H.replace TypeError (#63825).
No maintainer engagement on #64474 as of last check (2026-07-15); last human comment 2026-06-23. Filing with a full forensic chain so this doesn’t depend on a low-engagement thread.
What Should Happen?
Failed Bash tool calls in fullscreen TUI must render the same information classic TUI already shows:
⏺ Bash(echo "STDOUT-MARKER-ABC" && echo "STDERR-MARKER-XYZ" >&2 && exit 1)
⎿ Error: Exit code 1
STDOUT-MARKER-ABC
STDERR-MARKER-XYZ
At minimum: a visible ⎿ row with exit code and combined stdout/stderr (the same strings already stored in the session JSONL and already sent to the model). Success and failure paths should share the same render pipeline for the body; only styling (e.g. error color) should differ.
Error Messages/Logs
No CLI error. The TUI silently omits the result row. No stack trace, no “render failed” banner for the Bash body itself.
What the model received (same turn):
Exit code 1
STDOUT-MARKER-ABC
STDERR-MARKER-XYZ
What the session JSONL recorded (real session under ~/.claude/projects/.../<session>.jsonl, CC 2.1.170, Ghostty, fullscreen):
"content": [{
"type": "tool_result",
"content": "Exit code 1\nSTDOUT-MARKER-ABC\nSTDERR-MARKER-XYZ",
"is_error": true,
"tool_use_id": "toolu_011vzNHQr4967B7e5RZdFo1b"
}],
"toolUseResult": "Error: Exit code 1\nSTDOUT-MARKER-ABC\nSTDERR-MARKER-XYZ"
Both markers present. is_error: true. The harness did its job; only the fullscreen paint path drops the body.
What the user saw (verbatim):
⏺ Bash(echo "STDOUT-MARKER-ABC" && echo "STDERR-MARKER-XYZ" >&2 && exit 1)
Nothing under it. No ⎿. No Error.
Steps to Reproduce
- Open an interactive Claude Code session.
- Confirm renderer is fullscreen:
```
/tui
Current renderer: fullscreen
Expect: . If not, switch with /tui fullscreen`.
- Ask the model (or run via Bash tool) this exact command:
``bash``
echo "STDOUT-MARKER-ABC" && echo "STDERR-MARKER-XYZ" >&2 && exit 1
- Observe the live tool-call card.
Actual (fullscreen): only the ⏺ Bash(...) header; no ⎿ row.
Control A — success path (same session, still fullscreen):
echo "hello"
→ ⎿ hello appears. So capture + success render work.
Control B — failure path on classic renderer:
/tui default
Re-run the failing command → ⎿ Error: Exit code 1 + both markers appear. So the classic renderer is fine; bug is fullscreen-specific.
Control C — prove data integrity without relying on the UI:
Inspect the matching tool_result in the session JSONL (or ask the model what exit code / markers it saw). Content is present; only display is missing.
Claude Model
Not sure / Multiple models
(Model-independent — pure TUI renderer bug. Observed under Opus / Fable sessions; harness + display, not model generation.)
Is this a regression?
I don't know
(Unknown first-bad version. Still broken from at least 2.1.159 [#64474] through 2.1.170 and 2.1.185. Classic renderer remains correct on the same builds, so this is not “Bash stopped returning output.”)
Last Working Version
Unknown for fullscreen. Classic/default TUI is the working path on all versions tested.
Claude Code Version
2.1.170 — first forensic capture (JSONL + markers)
2.1.185 — live retest, ⎿ still completely absent in fullscreen
2.1.206 — current install at report time (`claude --version`)
#64474 original report: 2.1.159. No fix called out in changelog through published versions checked against this family (through at least 2.1.207).
Platform
Anthropic API
(Claude.ai / Max subscription interactive sessions; not Bedrock/Vertex-specific.)
Operating System
macOS
- Darwin 25.3.0, arm64 (Apple Silicon)
- Confirmed by a third party on Linux as well (#64474 comment: still broken on 2.1.186) — not macOS-only.
Terminal/Shell
Other
- Ghostty 1.3.2 /
TERM=xterm-256color/ zsh - Original #64474: iTerm2 — same class of bug, different terminal → not Ghostty-specific.
Additional Information
Impact
Any non-zero Bash exit (build failure, test failure, set -e pipeline, missing file, script crash) is invisible in the UI under the default-for-many-users fullscreen renderer. The model still “knows” and may narrate the failure correctly, but the user cannot audit the real stderr without:
- switching to
/tui default, or - digging in the session JSONL, or
- re-running via
! …REPL prefix /teeto a file.
This makes debugging agent sessions actively hostile: the most important tool results (failures) are the ones hidden.
Related issues (classified, not dups of this ticket)
| Issue | Relation |
|-------|----------|
| #64474 | Same surface (fullscreen + failed Bash). Our report is the stronger “row missing entirely” + forensic + workaround variant. |
| #63825 | Different mechanism (TypeError / H.replace on sandbox-violation annotation). Failure-class events can lose body, but not the same renderer branch. |
| #65880 / #63909 / #65166 / #68910 / #68383 | ENOSPC / statfs cluster — replaces empty-stdout+nonzero with a false “temp filesystem full” string. Opposite of blank: wrong string, not missing row. |
| #63966 | Earlier report: tool results empty then flush late/out-of-order (parallel/delivery). Different bug. |
| #27886 / #34713 | False “hook error” labels — can crowd the failure UI but do not swallow Bash stderr. |
Working workaround: PostToolUseFailure → systemMessage
The hook fires after the tool result is already captured. systemMessage still renders as a visible TUI warning even when the fullscreen path drops the ⎿ row. Confirmed working in fullscreen on 2026-06-23.
Minimal hook:
#!/usr/bin/env bun
// posttool-failure-hook.ts — surfaces failed Bash output via systemMessage
const hookData = JSON.parse(await Bun.stdin.text());
// On failure events, actual output is in `error` (toolResponse is often null/empty)
const error = typeof hookData.error === "string" ? hookData.error : "";
console.log(
JSON.stringify({
suppressOutput: false,
systemMessage: error ? `Tool failed — output: ${error}` : "Tool failed",
hookSpecificOutput: { hookEventName: "PostToolUseFailure" },
}),
);
process.exit(0);
Wire in ~/.claude/settings.json:
{
"hooks": {
"PostToolUseFailure": [
{
"hooks": [
{
"type": "command",
"command": "bun /path/to/posttool-failure-hook.ts"
}
]
}
]
}
}
Note: updatedToolOutput is not available on PostToolUseFailure (success-path PostToolUse only). systemMessage is the user-visible channel that works around the broken paint path.
Immediate UI workaround
/tui default
Classic renderer shows failed-Bash bodies correctly on the same versions where fullscreen drops them.
Diagnostic signature for others
/tui→fullscreen- Run any non-zero-exit Bash with known stdout markers
- UI: header only, no
⎿ - Ask model “what was the exit code / last line of that command?” → answers correctly from tool_result
rg "STDOUT-MARKER" ~/.claude/projects/.../<session>.jsonl→ hit intool_result.content
If (3) fails but (4)+(5) pass → this bug, not capture/ENOSPC/TypeError.
Ask
Please fix the fullscreen TUI failure branch so non-zero Bash results paint the same body classic already paints. The data is already in the render pipeline’s input (JSONL + model tool_result); only the failure-path paint is missing. Happy to attach a redacted JSONL excerpt or a screen recording if useful.