Skip to content

fix: tolerate non-message events in Claude tool traces - #122

Open
vlad902-bot wants to merge 3 commits into
TauCetiProject:mainfrom
vlad902-bot:fix/permission-denied-string-message
Open

fix: tolerate non-message events in Claude tool traces#122
vlad902-bot wants to merge 3 commits into
TauCetiProject:mainfrom
vlad902-bot:fix/permission-denied-string-message

Conversation

@vlad902-bot

@vlad902-bot vlad902-bot commented Aug 20, 2026

Copy link
Copy Markdown

This PR prevents Claude's stream-json tool-trace parser from crashing on stream events it does not consume. Claude Code 2.1.233 emitted a historical system/permission_denied event with a string message; _tool_trace assumed every event's message was an object, so an AttributeError escaped run_claude and ended a paid review round midway.

The current reviewer no longer exposes Bash after #123, so the observed trigger is not a live Bash path. The parser still accepts external CLI output, and only assistant and user messages belong in the tool trace. It now ignores other event kinds and keeps the trace parse inside the existing parse-error boundary, so an unexpected stream shape becomes a diagnosable failed attempt instead of unwinding the review engine.

The regression test replays the captured event, verifies that the terminal verdict and denied tool result still parse correctly, and checks that an unexpected parser exception is contained. python3 -m py_compile runner/*.py and every script in the tests workflow pass on the branch merged with current main.

🤖 Prepared with OpenAI Codex

(WARNING: This PR and this commit text is entirely AI-generated and
largely human-unreviewed.)

A claude reviewer runs with --allowedTools Read Grep Glob, so it can request a
tool the CLI refuses. When the refusal is settled before the ask, the stream
carries `{"type":"system","subtype":"permission_denied",...,"message":"<text>"}`
— a bare string where assistant/user events carry an object. `_tool_trace` read
`message` for every event before checking its type, so the string reached
`.get("content")` and the round died on an AttributeError.

It died three rubrics into TauCeti#3934, discarding correctness and reuse after
$4.22 of reviewing, and charged the PR one of its three review-error attempts.

Two changes. The parse now skips any event that is not an assistant or user
message, which is also the only kind it ever wanted. And the trace parse moves
inside `run_claude`'s handler: that handler exists so an unforeseen stream shape
degrades to a parse_error, and the one parse that could meet an unforeseen shape
sat above it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@kim-em kim-em changed the title fix: don't read a permission denial's message as a message object fix: tolerate non-message events in Claude tool traces Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants