paperclipai/paperclip · error
stop sandbox callback bridge timed out${detail ? `: ${detail
Error message
stop sandbox callback bridge timed out${detail ? `: ${detail}` : ""} What it means
Thrown by the `stop` callback of a started sandbox callback bridge when the kill-and-wait command ran longer than timeoutMs. The stop routine sends SIGTERM to the recorded pid, polls up to 40 times (≈2s) for the process to exit, then removes pid/ready files; if the whole execute() call reports timedOut, this error fires. It signals the remote bridge process did not die within the configured shutdown budget.
Source
Thrown at packages/adapter-utils/src/sandbox-callback-bridge.ts:1150
` pid="$(cat ${shellQuote(directories.pidFile)})"`,
" kill \"$pid\" 2>/dev/null || true",
" i=0",
" while kill -0 \"$pid\" 2>/dev/null && [ \"$i\" -lt 40 ]; do",
" i=$((i + 1))",
" sleep 0.05",
" done",
"fi",
`rm -f ${shellQuote(directories.pidFile)} ${shellQuote(directories.readyFile)}`,
].join("\n"),
),
cwd: input.remoteCwd,
env: {
[SANDBOX_EXEC_CHANNEL_ENV]: SANDBOX_EXEC_CHANNEL_BRIDGE,
},
timeoutMs,
});
if (stopResult.timedOut) {
throw new Error(buildRunnerFailureMessage("stop sandbox callback bridge", stopResult));
}
},
};
}
function getSandboxCallbackBridgeServerSource(): string {
return `import { randomUUID, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";
import { promises as fs } from "node:fs";
import path from "node:path";
const queueDir = process.env.PAPERCLIP_BRIDGE_QUEUE_DIR;
const bridgeToken = process.env.PAPERCLIP_BRIDGE_TOKEN;
const host = process.env.PAPERCLIP_BRIDGE_HOST || "127.0.0.1";
const port = Number(process.env.PAPERCLIP_BRIDGE_PORT || "0");
const pollIntervalMs = Number(process.env.PAPERCLIP_BRIDGE_POLL_INTERVAL_MS || "100");
const responseTimeoutMs = Number(process.env.PAPERCLIP_BRIDGE_RESPONSE_TIMEOUT_MS || "30000");
const maxQueueDepth = Number(process.env.PAPERCLIP_BRIDGE_MAX_QUEUE_DEPTH || "${DEFAULT_BRIDGE_MAX_QUEUE_DEPTH}");View on GitHub (pinned to 67001ec6eb)
Solutions
- Increase the timeoutMs passed to startSandboxCallbackBridge so teardown has more headroom.
- Check whether the bridge node process is genuinely gone on the remote host after the error (ps for the pid from directories.pidFile).
- Ensure the bridge server closes its HTTP server and pending connections on SIGTERM (review getSandboxCallbackBridgeServerSource signal handling).
- Clean stale pidFile/readyFile from directories before a fresh start to avoid pid reuse confusion.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await bridge.stop();
} catch (err) {
// Log and continue; the run is ending. Optionally attempt a forceful SIGKILL.
logger.warn('bridge stop failed/timed out', { error: (err as Error).message });
} Prevention
- Size timeoutMs for teardown based on observed bridge shutdown time.
- Ensure the bridge server closes HTTP connections and stops listening on SIGTERM.
- Clean stale pidFile/readyFile before each start to avoid pid-reuse confusion.
When it happens
Trigger: Calling the returned `stop()` function (bridge teardown at run end) where the remote shell execute timed out. Happens when the bridge node process ignores SIGTERM, the pidFile points at a zombie/reused pid, or the remote exec channel itself is stalled so even `kill` does not return.
Common situations: Bridge server has an open long-lived connection keeping the event loop alive; pid was recycled and now belongs to an unrelated process; remote host is under heavy load so the 40-iteration wait never sees exit; an earlier start left a stale pidFile pointing at a live process.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Sandbox callback bridge wrote invalid readiness JSON: ${erro
- Run log tail tick failed (exit ${result.exitCode ?? "null"}$
- ${action} timed out: ${result.stderr.trim()}
- Cloudflare sandbox bridge request timed out after ${requestT
- Timed out checking command "${command}" on sandbox target.
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/5cf671cbaa1a45a2.
Report an issue: GitHub.