Repository navigation
Hook callback errors (Stream closed) on every tool call since v0.1.29 #554
Description
Activity
My Environment
- OS: Zorin OS 18 (Ubuntu 24.04 Noble / Linux 6.14.0-37-generic x86_64)
- Python: 3.12.3
- Node.js: v24.13.0
- Claude Code CLI: 2.1.34 (system-installed)
- claude-agent-sdk: Tested on 0.1.29, 0.1.30, and 0.1.31 — all reproduce. Pinned back to 0.1.28 as workaround.
- Hardware: Desktop (MS-7C95 motherboard)
The error occurs consistently on every tool call — Read, Glob, Grep, Bash, WebFetch, WebSearch — all produce the same
Error in hook callback hook_0: ... Stream closedoutput. A single planning session with ~25 tool calls generates thousands of lines of noise in the logs.Confirmed: downgrading to
claude-agent-sdk==0.1.28(bundled CLI v2.1.30) completely eliminates the errors.Cleaned Logs
Here is a cleaned excerpt from a real agent session running with
claude-agent-sdk==0.1.31andpermission_mode="bypassPermissions". Every tool call follows the same pattern:Coding Style Analysis Phase (Opus model)
Using model: opus (claude-opus-4-5-20251101) | Tools: Read, Glob, Grep, Bash, WebFetch, WebSearch [claude 10:58:16] Using tool: Read → CLAUDE.md Error in hook callback hook_0: <~50 lines of minified CLI JS source> error: Stream closed at sendRequest (/$bunfs/root/claude:6344:133) ... [claude 10:58:16] Tool completed ← succeeds [claude 10:58:16] Using tool: Glob → *.{eslintrc,prettierrc,editorconfig} in . Error in hook callback hook_0: <~50 lines of minified CLI JS source> error: Stream closed at sendRequest (/$bunfs/root/claude:6344:133) ... [claude 10:58:16] Tool completed ← succeeds [claude 10:58:16] Using tool: Glob → **/biome.json in . Error in hook callback hook_0: <~50 lines of minified CLI JS source> error: Stream closed ... [claude 10:58:16] Tool completed ← succeedsPlanning Phase (same session, continues)
[claude 10:58:59] Using tool: Glob → ./tmp/missing-stuff/**/* in . Error in hook callback hook_0: <~50 lines of minified CLI JS source> error: Stream closed ... [claude 10:58:59] Tool completed ← succeeds [claude 10:59:00] Using tool: Bash → ls -la ./tmp/missing-stuff/ Error in hook callback hook_0: <~50 lines of minified CLI JS source> error: Stream closed ... [claude 10:59:00] Tool completed ← succeeds [claude 10:59:04] Using tool: Read → tmp/missing-stuff/0-priority-summary.md Error in hook callback hook_0: <~50 lines of minified CLI JS source> error: Stream closed ... [claude 10:59:04] Tool completed ← succeedsKey Observations
- Every tool type affected: Read, Glob, Grep, Bash — all produce the same error
- 100% reproduction rate: Not intermittent — happens on literally every tool call
- Non-fatal: Every tool completes successfully despite the error
- Minified source dump: The error includes lines 6339-6344 of the bundled Bun CLI source, including the
SubagentStophook handling code:hookEvent==="Stop"||hookEvent==="SubagentStop"
- Stack trace always identical: Always
sendRequest→callback→_U1at the same line numbers - Session works fine on v0.1.28: Zero errors, clean output, identical functionality
Raw Error Output
This is actual unedited output from a session using
query()withpermission_mode="bypassPermissions". Every tool call produces this exact block. Here are two consecutive tool calls:[claude 10:58:16] Using tool: Read → CLAUDE.md Error in hook callback hook_0: 6339 | Claude Code has been suspended. Run `fg` to bring Claude Code back. 6340 | Note: ctrl + z now suspends Claude Code, ctrl + _ undoes input. 6341 | `)},FL=()=>{l1((SL)=>SL+1)};return uI?.on("suspend",J$),uI?.on("resume",FL),()=>{uI?.off("suspend",J$),uI?.off("resume",FL)}},[uI]);let Cf=lA.useMemo(()=>b5(eA).filter(at),[eA]),kf=lA.useMemo(()=>{if(!ZA)return null;let J$=aL.filter((G9)=>G9.type==="progress"&&G9.data.type==="hook_progress"&&(G9.data.hookEvent==="Stop"||G9.data.hookEvent==="SubagentStop"));if(J$.length===0)return null;let FL=[...new Set(J$.map((G9)=>G9.toolUseID))],SL=FL[FL.length-1];if(!SL)return null;if(aL.some((G9)=>G9.type==="system"&&G9.subtype==="stop_hook_summary"&&G9.toolUseID===SL))return null;let SD=J$.filter((G9)=>G9.toolUseID===SL),BD=SD.length,XI=aL.filter((G9)=>{if(G9.type!=="attachment")return!1;let W1=G9.attachment;return"hookEvent"in W1&&(W1.hookEvent==="Stop"||W1.hookEvent==="SubagentStop")&&"toolUseID"in W1&&W1.toolUseID===SL}).length,R1=SD.find((G9)=>G9.data.statusMessage)?.data.statusMessage;if(R1)return BD===1?`${R1}…`:`${R1}… ${XI}/${BD}`;let yB=SD[0]?.data.hookEvent==="SubagentStop"?"subagent stop":"stop";ret | ... truncated 6342 | `);let A=await NVH(H,$);if(!A.success)throw Error(A.message);if(ND(`${mH.tick} ${A.message} 6343 | `),!A.alreadyUpToDate)c("tengu_plugin_updated_cli",{plugin_id:H,old_version:A.oldVersion||"unknown",new_version:A.newVersion||"unknown"});await Y0(0)}catch(A){lPH(A,`update plugin "${H}"`)}}var QgA=K(()=>{SI();_$();z$();MU();OVH()}) 6344 | `)}async sendRequest(H,$,A){let L=HEB.randomUUID(),I={type:"control_request",request_id:L,request:H};if(this.inputClosed)throw Error("Stream closed");if(A?.aborted)throw Error("Request aborted");await this.write(I);let D=()=>{this.write({type:"control_cancel_request",request_id:L});let B=this.pendingRequests.get(L);if(B)B.reject(new oE)};if(A)A.addEventListener("abort",D,{once:!0}) error: Stream closed at sendRequest (/$bunfs/root/claude:6344:133) at sendRequest (/$bunfs/root/claude:6344:627) at <anonymous> (/$bunfs/root/claude:6344:1305) at callback (/$bunfs/root/claude:6344:1451) at _U1 (/$bunfs/root/claude:3151:1288) at _U1 (/$bunfs/root/claude:3151:1525) at <anonymous> (/$bunfs/root/claude:3144:1239) at A (/$bunfs/root/claude:1240:1491) at MK$ (/$bunfs/root/claude:1240:1641) at iS (/$bunfs/root/claude:3144:5408) [claude 10:58:16] Tool completed [claude 10:58:16] Using tool: Glob → *.{eslintrc,prettierrc,editorconfig,eslintrc.js,eslintrc.json} in . Error in hook callback hook_0: 6339 | Claude Code has been suspended. Run `fg` to bring Claude Code back. 6340 | Note: ctrl + z now suspends Claude Code, ctrl + _ undoes input. 6341 | <same minified JS source repeated...> 6344 | <same sendRequest / Stream closed pattern...> error: Stream closed at sendRequest (/$bunfs/root/claude:6344:133) at sendRequest (/$bunfs/root/claude:6344:627) <same stack trace...> [claude 10:58:16] Tool completedThis repeats identically for every tool call in the session — Read, Glob, Grep, Bash, all affected. A typical agent session has 20-30 tool calls, producing thousands of lines of this error output.
so do i,
src.executor_sdk.hooks.stop_hook - INFO - [Hook-Stop] transcript预览: {"type":"queue-operation","operation":"dequeue","timestamp":"2026-02-11T06:35:02.202Z","sessionId":"bd89ccbc-ce9e-4e4b-80c9-9143d114f822"} {"parentUuid":null,"isSidechain":false,"userType":"external","cwd":"","sessionId":"","version":"2.1.37","gitBranch":"main","type":"user","message":{"role":"user","content":"{\n \"target\": \"/\",\n \"task_type\": \"recon\",\n \"task\": {\n \"id\": \"... Error in hook callback hook_1: 6350 | Claude Code has been suspended. Run \`fg\` to bring Claude Code back. 6351 | Note: ctrl + z now suspends Claude Code, ctrl + _ undoes input. 6352 | `)},JI=()=>{r9((SI)=>SI+1)};return mE?.on("suspend",JA),mE?.on("resume",JI),()=>{mE?.off("suspend",JA),mE?.off("resume",JI)}},[mE]);let ZD=lL.useMemo(()=>m_(eL).filter(Ds),[eL]),cD=lL.useMemo(()=>{if(!ZL)return null;let JA=aI.filter((Bf)=>Bf.type==="progress"&&Bf.data.type==="hook_progress"&&(Bf.data.hookEvent==="Stop"||Bf.data.hookEvent==="SubagentStop"));if(JA.length===0)return null;let JI=[...new Set(JA.map((Bf)=>Bf.toolUseID))],SI=JI[JI.length-1];if(!SI)return null;if(aI.some((Bf)=>Bf.type==="system"&&Bf.subtype==="stop_hook_summary"&&Bf.toolUseID===SI))return null;let R1=JA.filter((Bf)=>Bf.toolUseID===SI),f1=R1.length,XE=aI.filter((Bf)=>{if(Bf.type!=="attachment")return!1;let K9=Bf.attachment;return"hookEvent"in K9&&(K9.hookEvent==="Stop"||K9.hookEvent==="SubagentStop")&&"toolUseID"in K9&&K9.toolUseID===SI}).length,y9=R1.find((Bf)=>Bf.data.statusMessage)?.data.statusMessage;if(y9)return f1===1?`${y9}\u2026`:`${y9}\u2026 ${XE}/${f1}`;let h$=R1[0]?.data.hookEvent==="SubagentStop"?"subagent stop":"stop";ret | ... truncated 6353 | `);let L=await lWH(H,A);if(!L.success)throw Error(L.message);if(z1(`${cH.tick} ${L.message} 6354 | `),!L.alreadyUpToDate)d("tengu_plugin_updated_cli",{plugin_id:H,old_version:L.oldVersion||"unknown",new_version:L.newVersion||"unknown"});await PM(0)}catch(L){BQH(L,`update plugin "${H}"`)}}var imL=C(()=>{SE();KA();YA();_4();iWH()});function rzA(){return nmL.default.createElement(F,null,"MCP servers may execute code or access system resources. All tool calls require approval. Learn more in the"," ",nmL.default.createElement(pE,{url:"https://code.claude.com/docs/en/mcp"},"MCP documentation"),".")}var nmL;var rmL=C(()=>{uH();uH();nmL=j(s(),1)});function DU$({serverNames:H,onDone:A}){function L(E){let $=FI()||{},M=$.enabledMcpjsonServers||[],f=$.disabledMcpjsonServers||[],[D,U]=dnL(H,(G)=>E.includes(G));if(d("tengu_mcp_multidialog_choice",{approved:D.length,rejected:U.length}),D.length>0){let G=[...new Set([...M,...D])];EE("localSettings",{enabledMcpjsonServers:G})}if(U.length>0){let G=[...new Set([...f,...U])];EE("localSettings",{disabledMcpjsonServers:G})}A()}let I=fU$.useCallback(()=>{let $=(FI()||{}).disable | ... truncated 6355 | `)}async sendRequest(H,A,L){let I=aU$.randomUUID(),E={type:"control_request",request_id:I,request:H};if(this.inputClosed)throw Error("Stream closed");if(L?.aborted)throw Error("Request aborted");await this.write(E);let $=()=>{this.write({type:"control_cancel_request",request_id:I});let M=this.pendingRequests.get(I);if(M)M.reject(new H8)};if(L)L.addEventListener("abort",$,{once:!0});try{return await new Promise((M,f)=>{this.pendingRequests.set(I,{request:{type:"control_request",request_id:I,request:H},resolve:(D)=>{M(D)},reject:f,schema:A})})}finally{if(L)L.removeEventListener("abort",$);this.pendingRequests.delete(I)}}createCanUseTool(H){return async(A,L,I,E,$)=>{let M=await Z2(A,L,I,E,$);if(M.behavior==="allow"||M.behavior==="deny")return M;let f=await au9(A.name,$,L,I,M.suggestions);if(f)return f;try{H?.();let D=await this.sendRequest({subtype:"can_use_tool",tool_name:A.name,input:L,permission_suggestions:M.suggestions,blocked_path:M.blockedPath,decision_reason:ru9(M.decisionReason),tool_use_id:$,agent_id:I | ... truncated AbortError: at $ (/$bunfs/root/claude:6355:332) at I (/$bunfs/root/claude:1445:15515)- added a commit that references this issue
on Feb 15, 2026 This issue has the same root cause as #730 —
wait_for_result_and_end_input()applies a 60-second timeout that closes stdin prematurely, killing hook callbacks. The "Stream closed" errors on every tool call stem from this timeout shutting down the bidirectional control protocol.PR #731 removes the timeout entirely when hooks or MCP servers are active. Closing as duplicate of #730.
Bug Description
Since upgrading to
claude-agent-sdkv0.1.29+, every tool call produces noisyError in hook callback hook_0: Stream closederrors in the output. The errors are non-fatal (tools complete successfully and execution continues), but they spam the console with ~50 lines of minified Claude CLI source code per tool call, making logs unreadable.Environment
bypassPermissionsSteps to Reproduce
Error Output (per tool call)
This repeats for every single tool call in a session. A typical planning phase with 20+ tool calls produces hundreds of lines of error output.
Root Cause Analysis
v0.1.29 introduced new hook event types:
The error appears to originate from the parent process's hook callback trying to handle permission requests via `sendRequest` over a stream that's closed or not applicable when `bypassPermissions` mode is active. The hook fires, fails to communicate over the stream, throws `Stream closed`, but execution continues because permissions are bypassed.
The minified source visible in the error includes the `SubagentStop` hook handling code:
Workaround
Pinning to `claude-agent-sdk<0.1.29` (v0.1.28, CLI v2.1.30) eliminates the errors entirely.
Expected Behavior
Tool calls in `bypassPermissions` mode should not trigger hook callbacks that attempt stream communication, or the errors should be silently caught rather than dumped to stderr with minified source code.
Impact