fix(claude_code): parse message-form stream-json + raise on auth-failed (2.0.3)#205
Merged
Merged
Conversation
…n auth-failed (2.0.3)
Two follow-up bugs from the user's prod run after 2.0.2 unblocked the
streaming control flow:
1. ``output_len=0`` despite the pipeline reporting SUCCESS. Claude
Code 2.x's default stream-json output emits the full assistant
message in one envelope (``{"type":"assistant","message":{"content":
[{"type":"text","text":"..."}]}}``) — not the per-token delta
shape the parser was written for. The accumulator returned an
empty APIResponse for every session because its
``assistant.delta.text_delta`` branch never matched.
2. ``"Not logged in · Please run /login"`` came back as the
assistant's reply when the CLI's credential cache was empty.
The CLI annotates that frame with ``error=authentication_failed``
but otherwise looks like a normal assistant message; the parser
accepted it as legitimate output.
Fix:
- Introduce ``StreamJsonAccumulator`` — one shared parser used by
``create_message_stream`` (streaming caller) and
``assemble_response_from_stream_json`` (non-streaming caller).
Handles both stream-json shapes:
* delta form (``--include-partial-messages`` on, true streaming)
* full-message form (Claude Code 2.x default)
Synthesises per-block ``text_delta`` / ``thinking_delta`` /
``tool_use`` events when the message form arrives so UI consumers
see the same canonical shape they'd see with true streaming.
- Raise ``APIError(CLI_AUTH_FAILED)`` when the CLI tags a frame
with ``error="authentication_failed"``. Host surfaces the auth
problem to the user instead of running a successful pipeline that
silently returned a placeholder.
- Update the single-event translator
``stream_json_line_to_canonical_event`` to collapse a full
message's text blocks into one ``text_delta`` so legacy consumers
that don't use the accumulator still see content.
### Tests
- ``test_create_message_stream_message_form_collects_text``
- ``test_create_message_stream_authentication_failed_raises``
- ``test_send_streaming_message_form_text``
- New ``ok_message_form`` / ``message_form_auth_failed`` scenarios
in the fake binary mirror the real CLI 2.1.144 output.
Full ``tests/llm_client/`` — 187/187 pass.
2 tasks
CocoRoF
added a commit
to CocoRoF/Geny
that referenced
this pull request
May 19, 2026
…h_failed) (#812) 2.0.3 fixes two follow-on bugs the user hit on prod after 2.0.2: 1. Claude Code 2.x emits the full assistant message in one ``assistant.message.content[]`` envelope (not delta form) by default. The 2.0.2 accumulator only handled the delta form so sessions came back with ``output_len=0`` despite the pipeline reporting SUCCESS. 2. ``error="authentication_failed"`` envelopes were treated as normal assistant output. The "Not logged in · Please run /login" placeholder was returned as the agent's reply. See CocoRoF/geny-executor#205 for the full root-cause + fix. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two follow-up bugs from the user's prod run after 2.0.2 unblocked the streaming control flow:
1.
output_len=0despite the pipeline reporting SUCCESSClaude Code 2.x's default stream-json output emits the full assistant message in one envelope:
{"type":"assistant","message":{"content":[{"type":"text","text":"hi there"}]}}— not the per-token delta shape (
{"type":"assistant","delta":{"type":"text_delta",...}}) the parser was written for. The 2.0.2 accumulator returned an emptyAPIResponsefor every session because itsassistant.delta.text_deltabranch never matched. User saw:2. "Not logged in" placeholder came back as the assistant's reply
When the CLI's credential cache is empty it emits a normal-looking assistant frame:
{"type":"assistant","error":"authentication_failed","message":{"content":[{"type":"text","text":"Not logged in · Please run /login"}]}}The parser accepted it as legitimate output. User got a "successful" turn whose only content was the auth-failure placeholder.
Fix
StreamJsonAccumulator— one shared parser used by bothcreate_message_stream(streaming caller) andassemble_response_from_stream_json(non-streaming caller). Handles both shapes:--include-partial-messageson, true streaming)text_delta/thinking_delta/tool_useevents when the message form arrives so UI consumers see the same canonical shape they would with true streaming.APIError(CLI_AUTH_FAILED)when the CLI tags a frame witherror="authentication_failed". Host surfaces the auth problem to the user instead of running a successful pipeline that silently returned a placeholder.stream_json_line_to_canonical_event(single-event translator) collapses a full message's text blocks into onetext_deltafor legacy consumers that don't use the accumulator.Tests
test_create_message_stream_message_form_collects_text— message form yields text_deltas + populates terminal responsetest_create_message_stream_authentication_failed_raises— auth_failed envelope raises APIError(CLI_AUTH_FAILED)test_send_streaming_message_form_text— non-streaming caller picks up message-form text toook_message_form/message_form_auth_failedscenarios in the fake binary mirror real CLI 2.1.144 outputtests/llm_client/— 187/187 passRelease
2.0.3. Migration: none.