paperclipai/paperclip · warning
Chat SDK runtime is shutting down
Error message
Chat SDK runtime is shutting down
What it means
assertReplacementCurrent is the guard applied when installing a replacement chat SDK runtime for an endpoint. If the runtime is shutting down, it throws 'Chat SDK runtime is shutting down'; if the recorded replacement generation for the endpoint no longer matches, it throws the 'was superseded' error. This ensures only the newest replacement wins and none are installed during teardown.
Solutions
- Check runtime shutdown state before scheduling a replacement; skip replacements during teardown
- Re-read the latest generation for the endpoint and re-issue the replacement if superseded
- Serialize replacement installation per endpoint (queue or lock) to avoid generation races
- Treat the error as a benign no-op when it occurs during shutdown and let the new runtime take over
Example fix
// before
runtime.installReplacement(endpointId, generation);
// after
const latest = runtime.getReplacementGeneration(endpointId);
if (latest === generation && !runtime.isShuttingDown) {
runtime.installReplacement(endpointId, generation);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (runtime.isShuttingDown) return; // skip replacement during teardown
Try / catch
try { installReplacement(endpointId, generation); } catch (e) { if ((e as Error).message.startsWith('Chat SDK runtime')) { /* benign: superseded or shutting down; skip */ } else throw e; } Prevention
- Serialize per-endpoint replacement installation with a lock or queue
- Re-read the latest generation before installing and retry once if superseded
- Suppress this error as a no-op during process shutdown
- Log generation mismatches to detect racing restart routines
When it happens
Trigger: Installing a runtime replacement concurrently with runtime shutdown, or a replacement whose generation counter no longer equals the latest registered generation for that endpointId (an even newer replacement already claimed the slot).
Common situations: Service shutdown racing an in-flight endpoint swap; two restart/replacement flows racing for the same endpoint; a restart routine issuing a second generation before the first is installed.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Chat SDK endpoint runtime was retired
- ACPX provider ownership admission is already active
- ACPX runtime host is closing
- capability_live_attempt_not_running
- Capability live turn admission was abandoned during teardown
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-10).
Data as JSON: /api/errors/bd222170d08f8fa4.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/chat-sdk-runtime.ts:3236
if (!previous) return false;
this.endpoints.delete(endpointId);
this.retiringEndpoints.set(endpointId, previous);
await previous.shutdown();
this.retiringEndpoints.delete(endpointId);
return true;
}
private nextReplacementGeneration(endpointId: string): number {
const next = (this.replacementGenerations.get(endpointId) ?? 0) + 1;
this.replacementGenerations.set(endpointId, next);
return next;
}
private assertReplacementCurrent(
endpointId: string,
generation: number,
): void {
if (this.shuttingDown) throw new Error("Chat SDK runtime is shutting down");
if (this.replacementGenerations.get(endpointId) !== generation) {
throw new Error(
`Chat SDK runtime replacement for endpoint ${endpointId} was superseded`,
);
}
}
async replaceEndpoint(
options: CreateChatSdkEndpointRuntimeOptions,
): Promise<ChatSdkEndpointRuntime> {
if (this.shuttingDown) throw new Error("Chat SDK runtime is shutting down");
const generation = this.nextReplacementGeneration(options.endpointId);
return await this.enqueueLifecycle(options.endpointId, async () => {
this.assertReplacementCurrent(options.endpointId, generation);
await this.retireEndpoint(options.endpointId);
this.assertReplacementCurrent(options.endpointId, generation);
const next = createChatSdkEndpointRuntime(options);
this.endpoints.set(options.endpointId, next);View on GitHub (pinned to 3f1d897a7c)