[BUG][Typescript SDK] `canUseTool` callback in the Claude Code SDK is not working

Status Closed — not planned
Maintainer reply ✓ Yes — blois
Activity 11 comments · opened Jul 30, 2025 · closed Jan 10, 2026
💡 Likely answer: A maintainer (blois, collaborator) responded on this thread — see the highlighted reply below.

Environment

  • Platform (select one):
  • [X] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [ ] Other: <!-- specify -->
  • Claude CLI version: 1.0.63 (Claude Code)
  • Operating System: Ubuntu 24.04
  • Terminal: via Typescript SDK

Bug Description

The canUseTool callback in the Claude Code SDK hangs indefinitely. The callback is invoked correctly and returns the proper response format, but Claude Code never proceeds.

Steps to Reproduce

  1. Use the SDK with canUseTool callback:
  const { query } = require('@anthropic-ai/claude-code');

  const stream = query({
      prompt: (async function* () {
          yield { type: 'user', message: { role: 'user', content: 'What is 2+2?' } };
      })(),
      options: {
          canUseTool: (toolName, toolInput) => {
              return { behavior: "allow", updatedInput: toolInput };
          }
      }
  });

  for await (const message of stream) {
      if (message.type === 'result') break;
  }
  1. Run the script
  2. Observe that the callback is called but the script hangs indefinitely

Expected Behavior

Tool execution should proceed after canUseTool returns { behavior: "allow", updatedInput: toolInput }

Actual Behavior

The process hangs indefinitely after the callback is invoked. Claude Code never proceeds.

Potential Fix

I found a potential fix that seem to work for me after patching my local node_modules. Modify streamToStdin to keep the stdin stream open when canUseTool is used:

In node_modules/@anthropic-ai/claude-code/sdk.mjs:

  1. Change line ~374:
  // FROM:
  async function streamToStdin(stream, stdin, abortController) {
    // ... existing code ...
    stdin.end();
  }

  // TO:
  async function streamToStdin(stream, stdin, abortController, keepOpen = false) {
    // ... existing code ...
    if (!keepOpen) {
      stdin.end();
    }
  }
  1. Change line ~198:
// FROM:
streamToStdin(prompt, child.stdin, abortController);

// TO:
streamToStdin(prompt, child.stdin, abortController, !!canUseTool);

View original on GitHub ↗

11 Comments

blois collaborator · 1 year ago

Looking at a fix, the issue is that the input stream used to communicate the tool permission response is being closed when the prompt is being closed.

Workaround:

let done
let receivedResult = new Promise(resolve => {
    done = resolve;
})
const stream = query({
    prompt: (async function* () {
        yield { type: 'user', message: { role: 'user', content: 'echo "test" into test.txt' } };
        await receivedResult
    })(),
    options: {
        canUseTool: async (toolName, toolInput) => {
            return { behavior: "allow", updatedInput: toolInput };
        }
    }
});

for await (const message of stream) {
    if (message.type === 'result') {
        done()
    }
}
planger · 1 year ago

Thanks @blois! I can confirm that the workaround works (and is better than my very specific patch :-)).

blois collaborator · 1 year ago

Another fix will be arriving soon where Claude Code will not hang when this occurs and instead will reject the tool call (not desirable, but we need this to be reliable across all SDK surfaces).

d3lm · 1 year ago

@blois Any updates on this?

Advait1306 · 1 year ago
Another fix will be arriving soon where Claude Code will not hang when this occurs and instead will reject the tool call (not desirable, but we need this to be reliable across all SDK surfaces).

How does this work out? I do a couple of tasks that ask the user for permission and return true using this method. Will I have to refactor it to use the MCP option (which isn't ideal imho)

@blois

K-Mistele · 11 months ago

Any updates on this?

allfadersup · 10 months ago

Any updates on this?

gsabran · 10 months ago

This issue of requiring the input stream to remain open also impacted hooks for me. I made another report with repro steps.

github-actions[bot] · 8 months ago

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.

github-actions[bot] · 7 months ago

This issue has been automatically closed due to 60 days of inactivity. If you're still experiencing this issue, please open a new issue with updated information.

github-actions[bot] · 7 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.