paperclipai/paperclip · error
Chat SDK endpoint runtime was retired
Error message
Chat SDK endpoint runtime was retired
What it means
The Chat SDK runtime can be retired (shut down) while asynchronous work is still in flight. Every critical entry point calls assertNotRetired(); if the runtime has been retired, this error is thrown to prevent operating on a dead runtime (e.g. initialize() completing after shutdown).
Source
Thrown at server/src/services/chat-sdk-runtime.ts:2274
async initialize(): Promise<void> {
await this.initializeChat();
this.assertNotRetired();
if (this.provider === "discord" && this.discordGatewayEnabled) {
this.startDiscordGateway();
}
}
private async initializeChat(): Promise<void> {
this.assertNotRetired();
// The service still chooses when to initialize, after installing callback
// context. Retirement owns the settlement of that exact SDK operation.
this.initialization ??= this.chat.initialize();
await this.initialization;
this.assertNotRetired();
}
private assertNotRetired(): void {
if (this.retired) throw new Error("Chat SDK endpoint runtime was retired");
}
private startDiscordGateway(): void {
this.assertNotRetired();
if (this.discordGatewayTask) return;
const adapter = this.adapter as DiscordAdapter;
if (typeof adapter.startGatewayListener !== "function") return;
const abort = new AbortController();
this.discordGatewayAbort = abort;
this.discordGatewayTask = (async () => {
let rapidRestartCount = 0;
while (!abort.signal.aborted) {
const sessionStartedAt = Date.now();
let listener: Promise<unknown> | null = null;
try {
await adapter.startGatewayListener(
{
waitUntil: (task) => {View on GitHub (pinned to 01ad858492)
Solutions
- Check runtime lifecycle: do not call initialize/start methods after retire(); gate calls behind a readiness check.
- Serialize shutdown: await all in-flight initialization before retiring.
- Recreate a new runtime instance if the endpoint is needed again after retirement.
- In tests, await runtime disposal before ending the test to avoid post-teardown calls.
Example fix
// before
await oldRuntime.initialize(); // may throw after retire()
// after
if (!oldRuntime.isRetired()) {
await oldRuntime.initialize();
} else {
runtime = createRuntime(config); // fresh instance
} Defensive patterns
Strategy: try-catch
Validate before calling
if (runtime.isRetired?.()) throw new Error("runtime already retired; create a new one"); Type guard
null
Try / catch
try {
await runtime.initialize();
} catch (err) {
if (/was retired/.test(String(err?.message))) {
runtime = createRuntime(config); // recreate after shutdown
await runtime.initialize();
} else throw err;
} Prevention
- Track runtime lifecycle state in callers; never call into a retired runtime.
- Await in-flight work before retire() to avoid shutdown races.
- Serialize initialize and retire calls (mutex/queue).
- In tests, always await teardown before assertions that touch the runtime.
When it happens
Trigger: Calling initialize(), startDiscordGateway(), or other guarded methods after the runtime's retire()/shutdown path has set this.retired = true; a pending this.initialization promise resolving after retirement.
Common situations: Server shutdown or company teardown racing with an in-flight initialize; Discord gateway restart attempted after retirement; a request arriving during hot adapter reload/replacement; tests tearing down the runtime while a promise is still pending.
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 runtime is shutting down
- ACPX provider ownership admission is already active
- capability_live_attempt_not_running
- Capability live turn admission was abandoned during teardown
- PRP Codex transport is closed
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/514f3a676f03ca8d.
Report an issue: GitHub.