paperclipai/paperclip · error
Process tree has not exited.
Error message
Process tree has not exited.
What it means
After a non-completed process, the finally block issues DELETE with grace_ms=1000 to terminate the process tree. If the termination response reports tree_exited !== true, containment is not proven, so execute() throws this error (which the inner catch converts into CreateosCleanupError). It prevents silently leaving a live process in the sandbox.
Solutions
- Destroy the whole sandbox lease to guarantee containment rather than trusting process-level termination.
- Increase the grace_ms parameter if the API allows a longer termination window.
- Poll process status after DELETE to confirm exit before treating the run as complete.
- Investigate the command for processes that spawn detached children or trap signals.
Example fix
// before
if (termination.tree_exited !== true) throw new Error("Process tree has not exited.");
// after
if (termination.tree_exited !== true) {
await client.json(`${base}/${processId}?grace_ms=5000`, "DELETE", undefined, cleanupSignal);
} Defensive patterns
Strategy: validation
Try / catch
try {
const r = await execute(lease, cmd, { signal });
if (r instanceof CleanupUnconfirmed) await destroyLease(lease);
return r;
} catch (e) {
await destroyLease(lease); // containment unproven: reclaim sandbox
throw e;
} Prevention
- Destroy the lease whenever process termination is unconfirmed.
- Avoid commands that leave detached/trap-happy child processes.
- Allow a longer grace period for termination of large process trees.
When it happens
Trigger: The DELETE /processes/:id?grace_ms=1000 response returns tree_exited false — the process tree ignored SIGTERM/SIGKILL within the grace period, or the server reports exit asynchronously and the response races termination.
Common situations: Runaway or stuck child processes ignoring termination signals; very short grace_ms (1000ms) insufficient for a large process tree to die; server reporting state before reaping children.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- CreateOS command cleanup failed; process termination is…
- CreateOS lease cleanup is already in progress.
- CreateOS lease cleanup must finish before resume.
- CreateOS process cleanup is unconfirmed; destroy this lease…
- CreateOS process creation could not be confirmed; destroy…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/c2ebebd8a811985a.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/execute.ts:215
throw new CreateosCleanupError("CreateOS process creation could not be confirmed; destroy the lease before reusing it.");
}
if (signal.aborted && signal.reason?.name === "TimeoutError") {
output.finish();
return {
exitCode: null, timedOut: true, stdout: output.stdout, stderr: output.stderr,
metadata: { processId, outputTruncated: output.truncated },
};
}
if (signal.aborted) throw new Error("CreateOS command was cancelled.");
throw error;
} finally {
const cleanupSignal = AbortSignal.timeout(client.config.timeoutMs);
// Do not hide a cleanup failure: the host must know containment is unproven.
try {
if (processId && !completed) {
try {
const termination = await client.json(`${base}/${processId}?grace_ms=1000`, "DELETE", undefined, cleanupSignal);
if (termination.tree_exited !== true) throw new Error("Process tree has not exited.");
}
catch (error) { if (!(error instanceof CreateosApiError && error.status === 404)) throw new CreateosCleanupError("CreateOS command cleanup failed; process termination is unconfirmed."); }
}
} finally {
if (staged && stdinPath) {
// /files has no delete verb. /exec supplies a bounded, one-shot removal
// after the managed process finishes, without retaining another record.
await client.json(`/sandboxes/${id}/exec`, "POST", {
cmd: "/bin/rm", args: ["-f", "--", stdinPath],
}, cleanupSignal).catch(() => undefined);
}
}
}
}
View on GitHub (pinned to 3f1d897a7c)