paperclipai/paperclip · error · Error
ACPX runtime host is closing
Error message
ACPX runtime host is closing
What it means
startTurn() on the ACPX runtime host is refused once the host has been closed or a close sequence has begun (#closed or #closingStarted). The host is single-lifetime: after close() no new turns may be started.
Source
Thrown at packages/paperclip-runner/src/drivers/acpx/runtime-host.ts:603
}
goalSnapshot(): AcpxRuntimeGoalSnapshot | null {
return this.#runtime.goalSnapshot?.() ?? null;
}
async controlGoal(
action: "set" | "pause" | "resume" | "clear",
objective?: string,
): Promise<AcpxRuntimeGoalSnapshot | null> {
if (!this.#runtime.controlGoal) {
throw new Error("ACPX runtime does not expose session goal controls");
}
return await this.#runtime.controlGoal(action, objective);
}
startTurn(input: AcpxRuntimeTurnInput): AcpxRuntimeTurn {
if (this.#closed || this.#closingStarted) {
throw new Error("ACPX runtime host is closing");
}
if (this.#activeTurn) {
throw new Error("ACPX runtime host already has an active turn");
}
const requestId = boundedRequestId(input.requestId);
const text = boundedTurnText(input.text);
const turn = this.#runtime.startTurn({
text,
requestId,
...(input.signal ? { signal: input.signal } : {}),
...(input.onElicitation ? { onElicitation: input.onElicitation } : {}),
});
this.#activeTurn = turn;
void turn.result
.finally(() => {
// Once shutdown owns this turn, retain its cancellation handle until
// runtime cleanup succeeds. The result may settle while cleanup is
// failing, and a later close must still be able to retry cancellation.View on GitHub (pinned to 01ad858492)
Solutions
- Check the host's closed/closing state (or track it via close events) before calling startTurn
- Reopen a new runtime host via open() and submit the turn there
- Fix lifecycle management so no turn submissions are queued after close() is initiated
- Guard retries: on shutdown, drop pending turns instead of re-submitting
Example fix
// before
host.close();
host.startTurn({ text: "hi" }); // throws
// after
if (!host.isClosed && !host.isClosing) {
host.startTurn({ text: "hi" });
} else {
const fresh = await open({ /* same options */ });
fresh.startTurn({ text: "hi" });
} Defensive patterns
Strategy: try-catch
Validate before calling
function canStartTurn(host) {
return !host.isClosed && !host.isClosing;
} Type guard
function isOpenForTurns(host) {
return "isClosed" in host && host.isClosed === false && host.isClosingStarted === false;
} Try / catch
try {
host.startTurn({ text });
} catch (err) {
if (err.message === "ACPX runtime host is closing") {
const fresh = await open(options);
return fresh.startTurn({ text });
}
throw err;
} Prevention
- Stop turn producers before calling close()
- Track host lifecycle state in your scheduler
- Never retry startTurn on a host known to be closing
- Route post-shutdown requests to a freshly opened host
When it happens
Trigger: Calling startTurn() after close() was called, after an async close began (closingStarted set even if teardown is still in flight), or concurrently racing a close that started between an earlier availability check and the startTurn call.
Common situations: A request arrives while the session is shutting down; retry logic re-issues startTurn after the host already closed due to an earlier failure; lifecycle race where a scheduler keeps submitting turns to a closing host.
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
- ACPX provider ownership admission is closed
- ACPX provider ownership admission is already active
- Verified ACPX installation does not match its profile
- ACPX runtime does not expose session goal controls
- ACPX runtime host already has an active turn
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/e5319b3c98a24e4b.
Report an issue: GitHub.