openclaw - 💡(How to fix) Fix MCP child process leak: disposeSession() doesn't kill process groups [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#68809Fetched 2026-04-19 15:07:11
View on GitHub
Comments
1
Participants
2
Timeline
1
Reactions
0
Author
Timeline (top)
commented ×1

Root Cause

disposeSession() in model-context-tokens-z5hvDVkk.js (line ~1963) calls:

session.process.kill();

This sends SIGTERM to the direct child process only. But MCP servers configured with npx or uvx commands create a wrapper process chain:

openclaw spawns → npx (direct child) → npm exec → node obsidian-mcp (actual server)
openclaw spawns → uvx (direct child) → python mcp-obsidian (actual server)

When kill() terminates npx/uvx, the actual MCP server process survives and gets reparented to PID 1 (orphaned). Every agent session creates new MCP processes, and none of the underlying servers ever get cleaned up.

Fix Action

Fix / Workaround

Current Workaround

Code Example

session.process.kill();

---

openclaw spawns → npx (direct child) → npm exec → node obsidian-mcp (actual server)
openclaw spawns → uvx (direct child) → python mcp-obsidian (actual server)

---

// In createBundleLspToolRuntime(), change spawn to:
const child = spawn(launchConfig.command, launchConfig.args ?? [], {
  stdio: ["pipe", "pipe", "pipe"],
  detached: true,  // <-- creates process group
  env: { ...process.env, ...launchConfig.env },
  cwd: launchConfig.cwd
});

// In disposeSession(), change kill to:
try {
  process.kill(-session.process.pid, 'SIGTERM');  // <-- kills entire process group
} catch {
  session.process.kill();  // fallback
}

---

{
  "command": "node",
  "args": ["/path/to/node_modules/obsidian-mcp/build/main.js", "/path/to/vault"]
}
RAW_BUFFERClick to expand / collapse

Bug

openclaw-gateway leaks MCP child processes. Over 2+ days of uptime, my instance accumulated ~840 orphan processes (~5 GB RAM). After a restart, it hit 28 orphans in just 31 minutes.

Root Cause

disposeSession() in model-context-tokens-z5hvDVkk.js (line ~1963) calls:

session.process.kill();

This sends SIGTERM to the direct child process only. But MCP servers configured with npx or uvx commands create a wrapper process chain:

openclaw spawns → npx (direct child) → npm exec → node obsidian-mcp (actual server)
openclaw spawns → uvx (direct child) → python mcp-obsidian (actual server)

When kill() terminates npx/uvx, the actual MCP server process survives and gets reparented to PID 1 (orphaned). Every agent session creates new MCP processes, and none of the underlying servers ever get cleaned up.

Suggested Fix

Spawn MCP child processes with detached: true to create a process group, then kill the entire group on dispose:

// In createBundleLspToolRuntime(), change spawn to:
const child = spawn(launchConfig.command, launchConfig.args ?? [], {
  stdio: ["pipe", "pipe", "pipe"],
  detached: true,  // <-- creates process group
  env: { ...process.env, ...launchConfig.env },
  cwd: launchConfig.cwd
});

// In disposeSession(), change kill to:
try {
  process.kill(-session.process.pid, 'SIGTERM');  // <-- kills entire process group
} catch {
  session.process.kill();  // fallback
}

This ensures that when a session is disposed, the entire process tree (wrapper + actual server) is terminated.

Current Workaround

Replacing npx/uvx with direct binary paths in openclaw.json so there's no wrapper process to orphan:

{
  "command": "node",
  "args": ["/path/to/node_modules/obsidian-mcp/build/main.js", "/path/to/vault"]
}

Environment

  • macOS (Darwin 25.3.0, arm64)
  • openclaw v2026.4.2 (via Homebrew)
  • Node v24.14.1
  • MCP servers: obsidian-mcp (npm), mcp-obsidian (Python/uv)

extent analysis

TL;DR

To fix the issue of openclaw-gateway leaking MCP child processes, modify the createBundleLspToolRuntime() function to spawn child processes with detached: true and update the disposeSession() function to kill the entire process group.

Guidance

  • Verify that the issue is caused by the wrapper process chain created by npx or uvx commands by checking the process tree of the orphaned processes.
  • Apply the suggested fix by updating the createBundleLspToolRuntime() and disposeSession() functions as described.
  • As a temporary workaround, replace npx/uvx with direct binary paths in openclaw.json to avoid creating wrapper processes.
  • Test the fix by running the application for an extended period and checking for orphaned processes.

Example

const child = spawn(launchConfig.command, launchConfig.args ?? [], {
  stdio: ["pipe", "pipe", "pipe"],
  detached: true,  
  env: { ...process.env, ...launchConfig.env },
  cwd: launchConfig.cwd
});

try {
  process.kill(-session.process.pid, 'SIGTERM');  
} catch {
  session.process.kill();  
}

Notes

The suggested fix assumes that the createBundleLspToolRuntime() and disposeSession() functions are correctly implemented and that the issue is solely caused by the wrapper process chain. Additional testing and verification may be necessary to ensure the fix works as expected.

Recommendation

Apply the workaround by replacing npx/uvx with direct binary paths in openclaw.json until the suggested fix can be properly implemented and tested, as this provides a more immediate solution to the issue.

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

openclaw - 💡(How to fix) Fix MCP child process leak: disposeSession() doesn't kill process groups [1 comments, 2 participants]