[BUG] Claude Code TS SDK expects the prompt stream to remain open while working
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
If the prompt is an AsyncIterable<SDKUserMessage> that is closed before Claude Code is done working, several features (hooks, canUseTool) are not called.
What Should Happen?
All features should work if the prompt's async iterable has completed before Claude Code is done.
Error Messages/Logs
Error in hook callback hook_0: Error: Stream closed
Steps to Reproduce
Claude Code TS SDK requires the following type for the prompt parameter: string | AsyncIterable<SDKUserMessage>
When sending messages other than a simple text, we have to use AsyncIterable<SDKUserMessage>. Several features, such as hooks, or canUseTool do not work when the AsyncIterable is not kept open while Claude Code is working (ref, ref). Other core features work. When adding a stderr handle, we see a log such as: Error in hook callback hook_0: Error: Stream closed
here's the query setup (unchanged between repros):
const queryHandle = query({
prompt,
options: {
hooks: {
PreToolUse: [
{
hooks: [
async (input: HookInput): Promise<HookJSONOutput> => {
console.log(`🔧 About to run tool: ${input.tool_name}`)
return { continue: true }
},
],
},
],
},
stderr: (data: string) => {
console.error(`Claude Code stderr: '${data}'`)
},
},
})
This works:
// Text message
const prompt = "Can you read the readme?"
const queryHandle = ...
for await (const event of queryHandle) {
console.log(event)
}
This doesn't call the hook nor canUseTool, and stderr shows Error in hook callback hook_0: Error: Stream closed:
// Text message
const message: SDKUserMessage = { ... }
const prompt: AsyncIterable<SDKUserMessage> = {
async *[Symbol.asyncIterator]() {
yield message
},
}
const queryHandle = ...
for await (const event of queryHandle) {
console.log(event) // events are still logged, but the hook and canUseTool are not called as expected
}
keeping the prompt stream open fixes the issue:
let onReceiveResult: () => void = () => {}
const receivedResult = new Promise<void>((resolve) => {
onReceiveResult = resolve
})
const message: SDKUserMessage = { ... }
const prompt: AsyncIterable<SDKUserMessage> = {
async *[Symbol.asyncIterator]() {
yield message
await receivedResult
},
}
const queryHandle = ...
for await (const event of queryHandle) {
if (event.type == "result") {
onReceiveResult()
}
console.log(event)
}
Claude Model
None
Is this a regression?
No, this never worked
Last Working Version
No response
Claude Code Version
claude: 2.0.20 / @anthropic-ai/[email protected]
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
In addition to fixing this bug, I would recommend the TS SDK to not require an async parameter, ie to accept string | SDKUserMessage[] | AsyncIterable<SDKUserMessage>
Found 3 possible duplicate issues:
- https://github.com/anthropics/claude-code/issues/7905
- https://github.com/anthropics/claude-code/issues/4775
- https://github.com/anthropics/claude-code/issues/6223
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
🤖 Generated with Claude Code
I'm getting the same issue! I've tried to work around it by bumping how long the input stream stays open, but this doesn't seem like a sustainable solution long-term. Did you ever end up finding a solution?
bumping it is the way to go for now
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.