fix(templates): read long stream-json lines and bound Claude Code shutdown - #542
Open
michaelxu2288 wants to merge 2 commits into
Open
michaelxu2288 wants to merge 2 commits into
michaelxu2288 wants to merge 2 commits into
Conversation
…tdown The Claude Code scaffolds (sync, async and Temporal) and their tutorial copies read `claude -p --output-format stream-json` stdout through asyncio's StreamReader with the default 64 KiB line limit. Claude Code writes one JSON line per event, so a tool_result that echoes a large file read is a single line well past 64 KiB; readline() then raises "Separator is found, but chunk is longer than limit" and the turn aborts. Read with an 8 MiB limit, the same value `agentex agents run` uses for its child processes. Their cleanup also sent SIGTERM and then awaited proc.wait() with no bound, so a CLI that ignores or delays SIGTERM hangs request or activity cancellation. Wait at most 5 s, then SIGKILL. Verified with a fake `claude` on PATH, against the rendered sync and Temporal templates: - a 100,000-character line: main raises the ValueError, this branch reads it whole; - a CLI that ignores SIGTERM: main's aclose() is still hanging after 20 s, this branch returns after 5 s. tests/lib/cli/test_init_templates.py passes, and the three tutorials' test_agent_offline.py suites pass (7, 5 and 5 tests).
…ocess Render the sync Claude Code template, put a fake `claude` on PATH that prints a 100,000-character stream-json line and ignores SIGTERM, and drive _spawn_claude(): the long line must arrive whole and aclose() must return once the (shortened) grace period ends. Against main's template the test fails with "Separator is found, but chunk is longer than limit" after the unbounded wait sits out the child's 60 s sleep; here it passes in under 4 s.
This branch has not been deployed
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 Claude Code scaffolds (
sync-claude-code,default-claude-code,temporal-claude-code) and their tutorial copies (00_sync/060,10_async/00_base/130,10_async/10_temporal/140) share two bugs in how they read the CLI.1. Lines over 64 KiB abort the turn. stdout is read through asyncio's
StreamReaderwith its default 64 KiB limit (async for chunk in proc.stdoutisreadline()underneath). Claude Code writes one JSON line per event, so atool_resultthat echoes a large file read is a single line far past 64 KiB.readline()then raisesValueError: Separator is found, but chunk is longer than limit, and the turn fails before itsresultevent. The same class of bug was already fixed foragentex agents run(SUBPROCESS_STREAM_LIMITincli_utils.py).2. Cancellation can hang. Cleanup sends SIGTERM and then awaits
proc.wait()with no bound. A CLI that ignores or delays SIGTERM blocks request cancellation (sync/async ACP) or Temporal activity cancellation indefinitely.Fix
In each of the six files:
limit=STDOUT_LINE_LIMIT(8 MiB);TERMINATE_TIMEOUT_SECONDS(5 s), then SIGKILL;_spawn_claude's docstring.Nothing else changes. The Codex templates read with
read(4096)and usekill(), so neither bug applies to them. The Gemini CLI templates in #516 got the same treatment.Verification
I put a fake
claudeonPATHand drove_spawn_claude()from the rendered sync and Temporal templates.mainValueError: Separator is found, but chunk is longer than limitaclose()tests/lib/cli/test_init_templates.py: 26 passed.tests/test_agent_offline.py: 7, 5 and 5 passed.ruff checkis clean on the tutorial files, and all three compile.The PR appears safe to merge; the earlier missing-test concern is addressed.
What we checked:
Summary
Claude Code scaffolds now read stream-JSON lines up to 8 MiB and stop lingering CLI processes after a five-second grace period. The updates cover the generated templates and tutorial copies, with an offline subprocess test for both paths.
tool_resultlines can reach the turn without hitting asyncio’s default line limit.Reviews (2) · Last reviewed commit: "test(templates): run the Claude Code sca..."