openclaw - ✅(Solved) Fix spawn EBADF when gateway file descriptor count is high [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#77750Fetched 2026-05-06 06:21:57
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
2
Author
Timeline (top)
cross-referenced ×2commented ×1

Error Message

[process/supervisor] spawn failed: runId=neat-atlas reason=Error: spawn EBADF [tools] exec failed: spawn EBADF raw_params={"command":"bash /Users/loyu/.../healthcheck.sh","timeout":15}

Root Cause

The default stdinMode in createChildAdapter is "inherit" when no input is provided. When combined with the fd leak, the inherit + detached: true spawn fails with EBADF. The existing no-detach fallback doesn't help because it still uses "inherit" stdin and the same fd table is inherited.

Additionally, the fd leak itself indicates that some plugin is opening markdown files without closing them.

Fix Action

Fixed

PR fix notes

PR #77751: fix(process): avoid spawn EBADF by defaulting to pipe stdin for non-TTY gateways

Description (problem / solution / changelog)

Summary

When the gateway runs as a service (launchd/systemd), stdin is /dev/null and "inherit" mode exposes child processes to the parent's fd table. Combined with fd leaks from plugins (e.g. thousands of open markdown files from claude-mem/lossless-claw), spawn fails with EBADF because the kernel cannot handle the inflated fd table during posix_spawn.

Fixes #77750

Changes

  1. Default stdinMode to "pipe" when process.stdin is not a TTY — in interactive mode (TTY), behavior is unchanged. When running as a service, this avoids inheriting /dev/null and the parent's fd table.

  2. Always include a pipe-stdin fallback — adds a final fallback (detached: false + ["pipe", "pipe", "pipe"] stdio) so even if the first spawn fails with EBADF, the retry uses the safest configuration regardless of the original stdinMode.

  3. Import SpawnFallback type — needed for the typed fallback array construction.

Test plan

  • Tested on macOS 26.1 with openclaw gateway running via launchd
  • Before: spawn EBADF on every exec tool call (gateway had 14k fd leak)
  • After restart + patch: exec calls succeed, no EBADF errors
  • After restart: fd count normal (58), gateway API responding (HTTP 200)
  • Reproduced the pre-patch path: confirmed that with inherit stdin + high fd count, spawn fails; with pipe stdin it succeeds

AI-Assisted

This PR was written with Claude Code assistance. The human (loyu) verified the fix on their own machine — real gateway logs confirm the EBADF errors and the fix resolves them.

Changed files

  • src/process/supervisor/adapters/child.ts (modified, +16/-9)

Code Example

[process/supervisor] spawn failed: runId=neat-atlas reason=Error: spawn EBADF
[tools] exec failed: spawn EBADF raw_params={"command":"bash /Users/loyu/.../healthcheck.sh","timeout":15}
RAW_BUFFERClick to expand / collapse

Problem

The gateway process accumulates thousands of open file descriptors (observed 14,039, of which 13,982 are .md files likely from plugins like claude-mem/lossless-claw). When child_process.spawn is called via exec tool, it fails with Error: spawn EBADF because posix_spawn cannot handle the massive fd table.

Error logs

[process/supervisor] spawn failed: runId=neat-atlas reason=Error: spawn EBADF
[tools] exec failed: spawn EBADF raw_params={"command":"bash /Users/loyu/.../healthcheck.sh","timeout":15}

This affects ALL exec tool calls — MCP server startup, shell scripts, Python scripts, simple ls commands.

Root cause

The default stdinMode in createChildAdapter is "inherit" when no input is provided. When combined with the fd leak, the inherit + detached: true spawn fails with EBADF. The existing no-detach fallback doesn't help because it still uses "inherit" stdin and the same fd table is inherited.

Additionally, the fd leak itself indicates that some plugin is opening markdown files without closing them.

Fix approach

  1. src/process/supervisor/adapters/child.ts: Default stdinMode to "pipe" when process.stdin.isTTY is false (i.e., service-managed gateway). In service mode, stdin is /dev/null so "inherit" provides no benefit but exposes the child to fd table issues.

  2. src/process/supervisor/adapters/child.ts: Always add a pipe-stdin fallback that retries with ["pipe", "pipe", "pipe"] + detached: false, so even if the first spawn fails with EBADF, it retries with a safe configuration.

Environment

  • openclaw: 2026.5.3
  • OS: macOS 26.1 (Darwin 25.4.0)
  • Node: 25.9.0
  • Running via launchd as a gateway service

Related

Will open a PR with the fix.

extent analysis

TL;DR

To fix the issue, update the createChildAdapter in src/process/supervisor/adapters/child.ts to default stdinMode to "pipe" when process.stdin.isTTY is false and add a pipe-stdin fallback.

Guidance

  • Update src/process/supervisor/adapters/child.ts to change the default stdinMode to "pipe" when the process is not a TTY, as the current "inherit" mode exposes the child process to fd table issues.
  • Implement a pipe-stdin fallback that retries the spawn with ["pipe", "pipe", "pipe"] and detached: false to handle cases where the initial spawn fails with EBADF.
  • Verify the fix by checking if the Error: spawn EBADF error is resolved and if the exec tool calls are successful.
  • Investigate and address the fd leak caused by plugins opening markdown files without closing them.

Example

No code snippet is provided as the issue already outlines the necessary changes.

Notes

The fix approach outlined in the issue seems to address the root cause, but it's essential to test and verify the changes to ensure they resolve the issue without introducing new problems.

Recommendation

Apply the workaround by updating src/process/supervisor/adapters/child.ts as described, as it directly addresses the identified root cause and provides a clear solution to the problem.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING