Raise the CLI stdout line cap so image reads stop aborting turns - #424
Merged
Conversation
The Agent SDK caps one stream-json line at 1 MiB by default and kills the reader (and the turn) when a line exceeds it. A Read of a screenshot routinely does: the CLI ships the base64 twice per line and re-encodes anything over 2000 px. Expose agent.cli_max_message_bytes (64 MiB), pass it as max_buffer_size, refuse images that cannot fit under it in the Read hook, and name the cause in the error message.
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.
Problem
Sessions sometimes die mid-turn right after the agent
Reads an image, with14 occurrences in the gateway log since Jul 8, 6 of them in the last two days (screenshot-heavy UI work).
Root cause. The Agent SDK's
SubprocessCLITransportbounds one stream-json stdout line atmax_buffer_size, default 1 MiB (_DEFAULT_MAX_BUFFER_SIZE, unchanged upstream as of 0.2.152), and a line over the bound raisesCLIJSONDecodeErrorinside the reader task — fatal for the whole turn, not just the one tool call. Nerve never set the option. AReadof an image blows through 1 MiB easily because the CLItool_resultcontent block and the top-leveltool_use_result), andimage_limits {maxWidth: 2000, maxHeight: 2000, maxBase64Size: 5 MiB}), which can inflate a PNG.Measured with the bundled CLI (2.1.257): a 269 KB, 1280×2400 screenshot came out as a 1,304,261-byte line; a 420 KB, 1429×1370 one (no re-encode) as 1,121,668 bytes. So the crash threshold is roughly a 390 KB encoded image — ordinary Retina/full-page screenshots. Parallel reads are separate lines; one oversized image is enough.
The existing PreToolUse image validator only checked magic bytes and the 5 MB API limit (it was written for the poisoned-context bug), so these images sailed through it.
Fix
agent.cli_max_message_bytes(new, default 64 MiB) → passed asClaudeAgentOptions.max_buffer_size. The CLI caps a single image at 5 MiB of base64, so ~10.5 MB is the per-image worst case; 64 MiB leaves headroom for document blocks. Documented indocs/config.md.validate_image_file(..., max_message_bytes=)refuses an image whose encoded line (2 × base64 + envelope) cannot fit under the bound, so a too-big image fails as a tool error with a downscale hint instead of aborting the turn. The Read hook passes the configured bound.Testing
tests/test_image_validation.py(new): wire-size estimate (both copies, 5 MiB saturation), transport refusal vs. roomy cap, API limit still wins, bad magic still refused, non-image extensions ignored.tests/test_engine.py:max_buffer_sizereachesClaudeAgentOptions(default and override); Read hook denies an oversized image under a 1 MiB cap and allows it under the default.Takes effect on the next gateway restart.