Skip to content

Comments

fix: recover from non-JSON lines in stdout stream#588

Open
esensible wants to merge 1 commit intoanthropics:mainfrom
esensible:fix/json-buffer-non-json-recovery
Open

fix: recover from non-JSON lines in stdout stream#588
esensible wants to merge 1 commit intoanthropics:mainfrom
esensible:fix/json-buffer-non-json-recovery

Conversation

@esensible
Copy link

Summary

When non-JSON lines appear on stdout (e.g. verbose HTTP logs, sandbox debug messages, mTLS configuration output), the json_buffer accumulation logic in _read_messages_impl permanently poisons the buffer. Once any non-JSON content
enters the buffer, all subsequent json.loads() calls fail, causing the SDK to silently drop every remaining message.

In practice this manifests as query() returning incomplete results or the client hanging indefinitely.

  • Root cause: _read_messages_impl unconditionally appends every stdout line to json_buffer. A non-JSON line like 2026-02-19 [DEBUG] configureGlobalMTLS starting gets concatenated with the next valid JSON object, producing
    unparseable content that persists for the lifetime of the stream.
  • Fix: Try standalone json.loads() first (covers the common case of a complete JSON object on a single line). Only start buffering if the line starts with { (the stream-json protocol emits JSON objects). Non-JSON lines are logged
    at DEBUG level and skipped.

Fixes #347

Test plan

  • Added test_non_json_lines_dont_poison_buffer — simulates non-JSON log lines interleaved with valid JSON messages
  • Added test_non_json_lines_between_split_json — verifies non-JSON lines before a split JSON object don't corrupt the buffer
  • All 158 existing tests pass
  • ruff check, ruff format, mypy all clean
  • Validated in production Lambda environment (non-JSON mTLS debug lines no longer cause hangs)

🤖 Generated with Claude Code

When non-JSON lines appear on stdout (e.g. verbose HTTP logs, sandbox
debug messages, mTLS configuration output), the existing json_buffer
accumulation logic permanently poisons the buffer. Once any non-JSON
content enters the buffer, all subsequent json.loads() calls fail,
causing the SDK to silently drop every remaining message. In practice
this manifests as query() returning incomplete results or the client
hanging indefinitely.

Root cause: _read_messages_impl unconditionally appends every stdout
line to json_buffer. A non-JSON line like "2026-02-19 [DEBUG] ..."
gets concatenated with the next valid JSON object, producing
unparseable content that persists for the lifetime of the stream.

Fix: try standalone json.loads() first (covers the common case of a
complete JSON object on a single line). Only start buffering if the
line starts with "{" (the stream-json protocol emits JSON objects).
Non-JSON lines are logged at DEBUG level and skipped.

Fixes anthropics#347

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

Python SDK is broken in Docker environment with DEBUG env var and Claude Code 2.0.43+

1 participant