Skip to content

Claude Agent SDK import hangs under tsx watch + concurrently on Windows #1239

Description

@NavinChoudhary-0

Summary

Importing @anthropic-ai/claude-agent-sdk can cause a Node.js/TypeScript service to hang indefinitely during module initialization when the service is run with tsx watch under concurrently on Windows.

The same service starts successfully when run directly with tsx watch, and also starts successfully when using concurrently --raw.

This appears to be an interaction between:

  1. Windows process/stdio handling
  2. tsx watch
  3. concurrently's piped/prefixed stdio
  4. @anthropic-ai/claude-agent-sdk

Environment

  • OS: Windows
  • Runtime: Node.js
  • TypeScript runner: tsx
  • Process runner: concurrently
  • Claude dependency: @anthropic-ai/claude-agent-sdk

Expected behavior

The service should finish importing its dependencies and reach:

app.listen(4001)

regardless of whether it is launched directly or through concurrently.

Actual behavior

When launched through the normal concurrently configuration, the ingest process remains alive but hangs during module loading.

It never reaches app.listen(4001).

As a result, nothing is listening on port 4001, and another service attempting to call the ingest API receives a connection failure.

The process does not appear to crash or throw an exception. It remains alive with negligible CPU activity.

Process setup

The development command runs three services:

concurrently
├── web    → next dev
├── ingest → tsx watch
└── worker → tsx

Only the ingest service uses tsx watch.

The relevant dependency chain was:

ingest
  ↓
@relay/agents
  ↓
providers/claude.ts
  ↓
@anthropic-ai/claude-agent-sdk

Reproduction / isolation

The issue was isolated by testing the imports individually.

The hang occurs when importing:

@anthropic-ai/claude-agent-sdk

through the Claude provider.

The sibling Gemini provider using:

@google/genai

does not exhibit the same behavior.

Test 1: Run ingest directly

tsx watch packages/ingest/src/server.ts

Result:

Works

The server successfully reaches app.listen(4001).

Test 2: Run through concurrently

concurrently ...

with the normal piped/prefixed output.

Result:

Hangs during module initialization

The process remains alive but never reaches app.listen(4001).

Test 3: Run through concurrently with --raw

concurrently --raw ...

Result:

Works

This suggests that concurrently's normal stdio handling/prefixing is involved in the interaction.

Why this is significant

The Claude provider was statically imported by the provider registry:

import { ClaudeProvider } from "./providers/claude";

Although the registry constructs providers lazily, the static import causes the Claude provider and its dependency tree to be loaded during application startup.

In this application, ingest only uses the analyst and clarifier roles, both of which default to Gemini. A ClaudeProvider is therefore not constructed during ingest startup.

The Claude Agent SDK was nevertheless being loaded on every boot.

Workaround

The Claude provider was changed from an eager static dependency to a dynamic import inside the execution path:

const { ClaudeProvider } = await import("./providers/claude");

This removes the Claude Agent SDK from the ingest/web startup path.

The resulting behavior is:

Application startup
    ↓
Registry loads
    ↓
Gemini provider available
    ↓
app.listen(4001)
    ↓
Server ready

Claude requested later
    ↓
dynamic import()
    ↓
Claude Agent SDK loaded
    ↓
ClaudeProvider executes

This preserves:

  • concurrently
  • prefixed [web], [ingest], [worker] logs
  • tsx watch
  • ingest hot reload

without loading the Claude SDK during startup.

Questions

  1. Is @anthropic-ai/claude-agent-sdk expected to perform any process/stdio initialization during module import?
  2. Could the SDK's initialization interact with piped stdout/stderr or non-TTY streams?
  3. Is this combination of Windows + tsx watch + concurrently known to cause an import hang?
  4. If the SDK requires specific stdin/stdout behavior, could the import avoid performing that initialization until the SDK is actually instantiated/used?
  5. Is there any recommended configuration for running the SDK under concurrently and tsx watch on Windows?

Metadata

Metadata

Assignees

No one assigned

    Labels

    invalidThis doesn't seem rightquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions