spawn EBADF intermittent failure spawning Claude Code process via @anthropic-ai/claude-agent-sdk — regression in 2.1.152

Resolved 💬 1 comment Opened May 27, 2026 by matt-displayr Closed Jun 28, 2026

Description

Since auto-updating to Claude Code 2.1.152 today, tasks running through @anthropic-ai/claude-agent-sdk (v0.2.59) intermittently fail at spawn time with spawn EBADF. The error occurs before the Claude process starts — at the OS/libuv level when Node.js sets up stdio pipes for the new process. Previous versions did not exhibit this behaviour.

Error message

Error: spawn EBADF

Surfaced in SDK as: Failed to spawn Claude Code process: spawn EBADF

Environment

| | |
|---|---|
| Claude Code | 2.1.152 |
| @anthropic-ai/claude-agent-sdk | 0.2.59 |
| Node.js | v20.18.0 |
| macOS | 26.4.1 (Darwin 25.4.0) |
| Architecture | arm64 (Apple Silicon) |

Reproduction

The failure is intermittent — roughly 60–70% of spawn attempts fail once the issue manifests. Steps:

  1. Install/update to Claude Code 2.1.152
  2. Use @anthropic-ai/claude-agent-sdk query() to run a task with permissionMode: "plan" or "default", stdio: ["pipe", "pipe", "ignore"] (SDK default)
  3. Observe that some fraction of spawns fail immediately with spawn EBADF
  4. Re-running the same task may succeed or fail again

Timeline

  • Claude Code updated to 2.1.152: 2026-05-27T01:42 UTC
  • First spawn EBADF failure observed: 2026-05-27T01:47 UTC (5 minutes later)
  • 16 confirmed failures across the same session; zero failures recorded before this update

What I've ruled out

  • Not a missing binaryclaude binary exists at ~/.local/share/claude/versions/2.1.152, symlinked from ~/.local/bin/claude; existsSync check passes
  • Not file descriptor exhaustion (EMFILE)ulimit -n is 1,048,576; failures occur even on a freshly restarted server process
  • Not a cwd issue — working directory is a valid, accessible local path; invalid cwd would produce ENOENT not EBADF
  • Not a pre-aborted signalAbortController is created fresh per task
  • claude update confirms 2.1.152 is latest — no patch available

Suspected cause

spawn EBADF on macOS occurs at the posix_spawn level when libuv cannot correctly set up file descriptor actions for the child process. This may be an incompatibility between 2.1.152 and macOS Tahoe (Darwin 25.x) in how the binary's Mach-O metadata or entitlements interact with the OS's fd-table setup during spawn.

A secondary contributing factor may be that ProcessTransport.close() in the SDK ends processStdin but never destroys processStdout, leaking one read-pipe fd per completed task. Under macOS's posix_spawn implementation, a large open-fd table can push the EBADF threshold lower.

Workaround

Restarting the backend server process reduces (but does not eliminate) failure frequency.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗