spawn EBADF intermittent failure spawning Claude Code process via @anthropic-ai/claude-agent-sdk — regression in 2.1.152
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:
- Install/update to Claude Code 2.1.152
- Use
@anthropic-ai/claude-agent-sdkquery()to run a task withpermissionMode: "plan"or"default",stdio: ["pipe", "pipe", "ignore"](SDK default) - Observe that some fraction of spawns fail immediately with
spawn EBADF - 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 EBADFfailure 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 binary —
claudebinary exists at~/.local/share/claude/versions/2.1.152, symlinked from~/.local/bin/claude;existsSynccheck passes - Not file descriptor exhaustion (EMFILE) —
ulimit -nis 1,048,576; failures occur even on a freshly restarted server process - Not a
cwdissue — working directory is a valid, accessible local path; invalidcwdwould produceENOENTnotEBADF - Not a pre-aborted signal —
AbortControlleris created fresh per task claude updateconfirms 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.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗