Paperclip

Paperclip finds 205 orphaned wrappers holding 10.5 GB

A Paperclip repair says one cleanup found 205 stray process wrappers from three runs holding about 10.5 GB. Related records show eight liveness probes disagree on EPERM and one timed-out MCP call can hide an entire server’s tool catalog.

← Back to homeOriginal source ↗

paperclipai/paperclip issue #11913 is the inspected primary source: “Liveness checks disagree on EPERM: eight process.kill(pid, 0) implementations, one treats a live process as dead.” The related records below were inspected as supporting context rather than independent confirmation.

The facts

- paperclipai/paperclip issue #11913 was created 2026-08-21T23:12:46Z and was open at inspection; its title is “Liveness checks disagree on EPERM: eight process.kill(pid, 0) implementations, one treats a live process as dead.” - paperclipai/paperclip PR #11916 was created 2026-08-21T23:51:19Z and was open at inspection; its title is “fix(adapter-utils): reap process session wrappers instead of orphaning them.” - paperclipai/paperclip PR #11910 was created 2026-08-21T22:32:45Z and was open at inspection; its title is “fix(tool-gateway): keep MCP connections callable after one timed-out tools/call.” - The repository reported 79098 stars, 5212 open issues, default branch master, and last push 2026-08-22T00:40:22Z. - The latest tagged-release baseline checked was v2026.817.0 published 2026-08-18T03:17:29Z.

What changed

paperclipai/paperclip issue #11913 reports: What happened?** process.kill(pid, 0) has three outcomes, not two, and the codebase has eight separate implementations of the liveness check that disagree about the third. No throw — the process exists and is signalable. ESRCH — the process genuinely does not exist. The only proof of death. EPERM — the process **exists** but this user may not signal it. Routine on Windows, where OpenProcess returns ERROR_ACCESS_DENIED for a process left by an elevated or different-session run, and on POSIX for another user's process. Anything else — unknown. Not proof of death. Current state of the eight sites: | Location | EPERM | Unrecognised errno | |---|---|---| | server/src/services/local-service-supervisor.ts:245 | **dead** | **dead** | | cli/src/install-store.ts:154 | alive | dead | | cli/src/commands/worktree.ts:2105 | alive | dead | | server/src/services/heartbeat.ts:6337 | alive | dead | | server/src/services/hot-restart.ts:145 | alive | dead | | server/src/services/workspace-instance-cleanup.ts:98 | alive | dead | | server/src/services/workspace-operations.ts:106 | alive | dead | | cli/src/commands/service.ts:67 | alive | alive | Only service.ts is fail-safe in both columns. Only local-service-supervisor.ts gets EPERM wrong. Expected behavior** One implementation, used everywhere, that distinguishes alive / dead / unknown and resolves anything short of ESRCH toward "still running". #11871 added exactly this as probeProcessLiveness in packages/db/src/embedded-postgres-lock.ts, with a tri-state ProcessLiveness return, because collapsing the third case is what let a live PostgreSQL

paperclipai/paperclip PR #11916 reports: Problem The process session wrapper is launched with nohup... &, so it leaves the run's process group and reparents to init as soon as its launching shell exits. Nothing supervises it after that, which makes leaving on its own the only thing that bounds its lifetime — and it had no reliable way to do so. A clear-out on one instance killed **205 strays across three runs holding ~10.5 GB**, some four days old. Three gaps kept the wrapper resident: 1. **The event-file wrapper polled forever after its own child closed**, waiting for a stdinEnd that only arrives if the host is still alive to send one. The streamed variant already ended its poll here; the event-file one did not. 2. **stop() raced itself.** It wrote that stdinEnd and then removed the session directory on the very next line. The wrapper reads stdin on a 50 ms poll, so the removal could delete the request before the wrapper ever saw it — leaving it polling a directory that no longer exists. 3. **Nothing covered a host that died without running stop()** at all (a crash or restart). Fix Teardown is now covered three independent ways, because each covers a case the others miss: **Child close ends the wrapper** — the normal path, and the one that leaked on every

paperclipai/paperclip PR #11910 reports: Thinking Path - Paperclip is the open source app people use to manage AI agents for work - Agents call company MCP servers through the connected-tool gateway - The gateway records one tools/call timeout or JSON-RPC error as connection healthStatus=error - Listing then admits only ok and healthy connections, so every tool on that server disappears - Recovery then needs a separate board health-check instead of a retry of the same tool - This pull request records the failed invocation and leaves the connection callable - The benefit is that one slow or failed operation does not hide the rest of the catalog Linked Issues or Issue Description Refs #10147 (first comment: do not flip connection-wide health on one operation timeout). This change does not make named MCP gateways honor timeoutMs. That remaining gap stays on #10147. Related open PR search (2026-08-21): no in-flight PR for this latch. Nearby MCP HTTP work: #9750 (initialize handshake; different problem). What happened?** One remote tools/call that hits the default 10s tool_timeout (or a JSON-RPC error) marks the whole MCP connection unhealthy. Later calls return tool_not_found for every tool on that connection. Expected behavior** The timed-out call still fails with tool_timeout / remote_mcp_error. The connection stays listed. A later call or health-check can still reach the same server. Steps to reproduce** 1. Connect a

Why it matters

Process liveness and connection health are both three-state problems. Collapsing permission-denied or one operation timeout into death can delete ownership records, strand memory and hide still-usable tools. The operator test is whether the system remains bounded and its receipts still describe the action after failure, retry or restart.

Current

The primary record was open when captured on 2026-08-22. The tagged-release baseline was v2026.817.0 published 2026-08-18T03:17:29Z. Repository metadata, full source bodies, current pull requests and issues, releases, Google News, Hacker News, Lobsters, Metamesh and the rendered ClawCharts row were inspected. ClawCharts selected the subject; it did not prove the claim.

Evidence

The primary URL is https://github.com/paperclipai/paperclip/issues/11913. Supporting records are https://github.com/paperclipai/paperclip/pull/11916 and https://github.com/paperclipai/paperclip/pull/11910. Source bodies, timestamps and states are preserved in the daily evidence bundle. Test counts and reproductions remain attributed to their authors unless identified as independently rerun.

Source boundary

Open work is described as open, closed work as closed, and operator reports as reports. A pull request is evidence of proposed or reviewed direction, not proof of a shipped release. Search residue, package mirrors and historically published source spines were excluded.

Operator take

Centralize tri-state liveness, reap wrappers through independent exit paths, and keep connection-wide health separate from a single tool-call result. Preserve a before-state receipt, make the smallest reversible change, and verify the original failure independently.

Caveat

Public project records are mutable. Status, scope and evidence can change after publication. This brief records the inspected state and does not authorize changes to a reader’s deployment.

Source inspected; source state, environment and release boundary remain explicit.