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

Resolved 💬 11 comments Opened Jul 30, 2025 by planger Closed Jan 10, 2026

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 ↗

This issue has 11 comments on GitHub. Read the full discussion on GitHub ↗