fix(bridge): pass explicit max_buffer_size to the Claude SDK - #908
Merged
Conversation
The Claude adapter never set ClaudeAgentOptions.max_buffer_size, so the SDK fell back to its own _DEFAULT_MAX_BUFFER_SIZE of 1 MiB (claude_agent_sdk/_internal/transport/subprocess_cli.py). A single stdout NDJSON line above that limit raises SDKJSONDecodeError inside the message reader task, which has no recovery path: the whole turn dies with "JSON message exceeded maximum buffer size of 1048576 bytes". Measured on this node 2026-08-03 18:19:14 KST. The fatal line was 1,056,854 bytes -- only 8,278 bytes over the limit. A 510 KB PNG screenshot went through the Read tool; Claude Code resized and re-encoded it to 682x2000 (528,000 base64 chars) and then shipped that same base64 twice in one message, as message.content[0].content[0].source.data and again as toolUseResult.file.base64. The duplication doubles the payload, so the effective failure threshold was only ~524 KB of base64 for a single image -- routinely reached by ordinary screenshot work. Every construction path now passes an explicit bound, including bare settings-free ClaudeRuntime() (unit tests, conformance harness), which would otherwise remain a route back to 1 MiB. The value is configurable via the new CCC_CLAUDE_MAX_BUFFER_SIZE (default 16 MiB, accepted range 1 MiB - 256 MiB); the 1 MiB floor is rejected-below because anything lower is strictly worse than the SDK default this bound exists to replace. Malformed settings degrade to the default instead of failing session start. The default is single-sourced in runtime_config_check.py (stdlib-only, already the home of DEFAULT_PROCESS_TIMEOUT_SECONDS) so config.py and claude_runtime.py cannot drift. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
seoseo-ai
approved these changes
Aug 3, 2026
seoseo-ai
left a comment
Collaborator
There was a problem hiding this comment.
Approved after explicit operator authorization using the local seoseo-ai credential.
This was referenced Aug 3, 2026
seoseo-ai
added a commit
that referenced
this pull request
Aug 3, 2026
…#909) (#917) No self-update agent-cron task was ever registered, so a deferred run (bridge busy) had no scheduled tick to retry on — a node could go days without receiving merged fixes (gwakga: timer active, task absent, so #908 never applied). setup.sh now idempotently adds a `self-update` task right after the self-update hook is installed: - `add` rejects a duplicate id, so re-runs are a no-op (idempotent). - Opt out with CCC_SELF_UPDATE_REGISTER_CRON=false. - Schedule via CCC_SELF_UPDATE_CRON (default `17 4,10,16,22 * * *`). - --success-exit-codes 0,8,11 so a clean update, a bridge-busy defer (8), and a no-services-allowlist degraded run (11) do not raise on-failure alerts; only real aborts alert. (Uses the #911 successExitCodes feature.) - The agent-cron timer is still installed separately; without it the task never fires. docs/self-update.md gains a Scheduling section and stops claiming "the next scheduled tick retries" without that precondition; it also notes that an update requested from a live conversation self-defers (its own turn marks the bridge busy). Tests: setup.test.sh 69 pass; bash -n clean. Co-authored-by: Seo Jin On <jinon86@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 3, 2026
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
The bridge lost a whole turn on 2026-08-03 18:19:14 KST:
Cause chain (all verified against the installed SDK,
claude-agent-sdk0.2.128):claude_agent_sdk/_internal/transport/subprocess_cli.py:30—_DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024(1 MiB)options.max_buffer_sizeisNone, that 1 MiB default is usedSDKJSONDecodeError, killing the message reader task. There is no recovery path, so the whole turn fails.max_buffer_sizeanywhere, so it always ran on the 1 MiB default.The line that actually killed the reader was 1,056,854 bytes — only 8,278 bytes over the limit. A 510 KB PNG screenshot went through the Read tool; Claude Code resized and re-encoded it to 682x2000 (528,000 base64 chars) and then shipped that same base64 twice in one message: once as
message.content[0].content[0].source.dataand again astoolUseResult.file.base64. That duplication doubles the payload, so the effective failure threshold is only ~524 KB of base64 for a single image — reached routinely by ordinary screenshot/image work.Change
ClaudeRuntime._build_optionsnow always passes an explicitmax_buffer_size. The value is resolved once in__init__, deliberately outside the settings-bound branch, so the bare settings-free construction path (unit tests, the conformance harness, any future caller that forgets to bind settings) cannot fall back to 1 MiB either.New setting, following the existing
alias=convention inbridge/utils/config.py:CCC_CLAUDE_MAX_BUFFER_SIZE16777216(16 MiB)Configrejects it.runtime_config_check.py(stdlib-only, already the home ofDEFAULT_PROCESS_TIMEOUT_SECONDS) soconfig.pyandclaude_runtime.pycannot drift.Per repo convention the measured numbers are recorded in the code comments, not just here.
Files
bridge/runtime_config_check.py—DEFAULT_CLAUDE_MAX_BUFFER_SIZE/MIN_/MAX_+ incident rationalebridge/utils/config.py—claude_max_buffer_sizefield, aliasCCC_CLAUDE_MAX_BUFFER_SIZEbridge/core/claude_runtime.py—_resolve_max_buffer_size(); explicitmax_buffer_size=onClaudeAgentOptionsbridge/tests/test_claude_max_buffer_size.py— new, 19 testsbridge/.env.example,bridge/README.md,bridge/CLAUDE.md— operator discoverabilityCHANGELOG.md,bridge/CHANGELOG.mdTests
New suite covers (a) the default, (b) env override + out-of-range rejection, (c) the value actually reaching
ClaudeAgentOptionson both the settings-bound and bare paths, plus a guard pinning the SDK's own 1 MiB constant so the rationale cannot go stale silently.Source-only change. No live tree, service, or deployment was touched.
🤖 Generated with Claude Code