paperclipai/paperclip · error · ToolGatewayHttpError
err.reasonCode
err.reasonCode
Error message
err.message
What it means
During slot acquisition for a gateway tool call, a ToolRuntimeSupervisorError thrown by the runtime supervisor is converted into a ToolGatewayHttpError preserving its HTTP status, message, reasonCode, and details. This surfaces supervisor-level slot failures (e.g. capacity limits, stuck slots, restart storms) as structured HTTP errors to the caller.
Solutions
- Inspect err.reasonCode and err.status to identify the supervisor failure (capacity vs startup vs stuck slot).
- Stop unused slots or wait for idleTtlMs (1s default) / restart backoff to elapse, then retry the tool call.
- Raise maxCompanySlots/maxHostSlots in ToolRuntimeSupervisorOptions if limits are legitimately too low.
- Check the tool access audit events (toolAccessAuditEvents) for the restart/backoff history of the slot.
Example fix
// before
await executeGatewayTool(input); // throws ToolGatewayHttpError (supervisor)
// after
try {
await executeGatewayTool(input);
} catch (e) {
if (e instanceof ToolGatewayHttpError && e.reasonCode === "slot_capacity") {
await stopIdleSlots(companyId);
return executeGatewayTool(input); // retry after capacity freed
}
throw e;
} Defensive patterns
Strategy: retry
Validate before calling
const slots = await getCompanySlots(companyId); if (slots.filter(s => ["starting","running","idle"].includes(s.status)).length >= 4) await stopIdleSlots(companyId);
Type guard
function isSupervisorGatewayError(e: unknown): e is ToolGatewayHttpError {
return e instanceof ToolGatewayHttpError && typeof e.reasonCode === "string" && e.reasonCode.startsWith("slot");
} Try / catch
try {
return await executeGatewayTool(input);
} catch (e) {
if (isSupervisorGatewayError(e) && [429, 503].includes(e.status)) {
await backoffRetry(() => executeGatewayTool(input), { attempts: 3, baseMs: 1000 });
}
throw e;
} Prevention
- Monitor active slot counts per company against maxCompanySlots and free idle slots proactively.
- Set explicit idleTtlMs suited to your workload instead of relying on the 1s default.
- Alert on restart-storm reason codes; they usually mean the runtime binary is crashing.
When it happens
Trigger: Calling executeGatewayTool when the supervisor cannot allocate/restart the runtime slot: company slot limit (maxCompanySlots) reached, host slot limit reached, restart storm detected, slot stuck beyond stuckSlotMs, or the runtime process failed to start.
Common situations: A company runs more than maxCompanySlots (4) concurrent tool connections; the runtime binary crashes repeatedly triggering backoff/storm protection; an idle slot expired between check and use; deployment mode/exposure misconfiguration.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- ACPX runtime does not expose session goal controls
- ACPX runtime host already has an active turn
- ACPX runtime host is closing
- Announcement request failed
- Anthropic Managed Agents request failed with HTTP
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/286525c3505c9e1d.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/tool-gateway.ts:10890
async listRuntimeSlots(companyId?: string) {
return runtimeSupervisor.listSlots(companyId);
},
async stopRuntimeSlot(input: {
companyId: string;
slotId: string;
actor?: { agentId?: string | null; runId?: string | null };
}) {
try {
return await runtimeSupervisor.stopSlot({
companyId: input.companyId,
slotId: input.slotId,
agentId: input.actor?.agentId ?? null,
runId: input.actor?.runId ?? null,
});
} catch (err) {
if (err instanceof ToolRuntimeSupervisorError) {
throw new ToolGatewayHttpError(
err.status,
err.message,
err.reasonCode,
err.details,
);
}
throw err;
}
},
async restartRuntimeSlot(input: {
companyId: string;
slotId: string;
actor?: { agentId?: string | null; runId?: string | null };
}) {
try {
return await runtimeSupervisor.restartSlot({
companyId: input.companyId,View on GitHub (pinned to 3f1d897a7c)