paperclipai/paperclip · error
Warm run identity rotation requires a durable transition rec
Error message
Warm run identity rotation requires a durable transition receipt.
What it means
rotateRunIdentity() was called with no warm transition and no completed transition receipt in the durable store, while the command log already contains a "run.attach" command. Identity rotation without a durable transition receipt is only allowed on a fresh (never-attached) store; an existing run.attach command means the run was previously attached, and its identity can only rotate through a proper warm transition with a receipt.
Source
Thrown at packages/paperclip-runner/src/control-plane/durable-prp-control-plane.ts:1670
if (
canonicalJson(runAttachTemplate) !== canonicalJson(expectedTemplate)
) {
throw new Error(
"Warm run transition template conflicts with its exact command.",
);
}
const candidate = structuredClone(this.#store.state);
candidate.runAttachTemplate = structuredClone(runAttachTemplate);
this.#store.commit(candidate);
}
return;
}
if (
this.#store.state.commands.some(
(command) => command.type === "run.attach",
)
) {
throw new Error(
"Warm run identity rotation requires a durable transition receipt.",
);
}
if (
!Object.values(identity).every(
(value) => typeof value === "string" && stableIdPattern.test(value),
) ||
identity.runnerInstanceId !== this.#identity.runnerInstanceId ||
identity.environmentLeaseId !== this.#identity.environmentLeaseId ||
identity.normalizedSessionId !== this.#identity.normalizedSessionId ||
identity.runId === this.#identity.runId ||
this.#store.state.commands.some((command) => command.status === "pending")
) {
throw new Error("Durable PRP run identity rotation is invalid.");
}
this.disconnectActiveRunner();
const leases = Object.fromEntries(
Object.entries(this.#store.state.leases).map(([key, lease]) => [View on GitHub (pinned to 01ad858492)
Solutions
- Perform the warm transition flow first so a durable receipt exists, then rotate via rotateRunIdentity.
- If the store should be fresh, start from a new store/identity instead of rotating an attached one.
- Recover from a backup/snapshot of the store that includes the transition receipt.
- Audit why warmTransition and completedWarmTransition are both absent while run.attach history exists (compaction bug) before proceeding.
Example fix
// before controlPlane.rotateRunIdentity(newIdentity); // store already has run.attach, no receipt // after await performWarmTransition(newIdentity); // creates durable receipt controlPlane.rotateRunIdentity(newIdentity, attachTemplate);
Defensive patterns
Strategy: validation
Validate before calling
const hasAttach = store.state.commands.some((c) => c.type === "run.attach");
const hasReceipt = store.state.warmTransition != null || store.state.completedWarmTransition != null;
if (hasAttach && !hasReceipt) {
throw new Error("Cannot cold-rotate: run attached without durable receipt; use warm transition recovery.");
} Try / catch
try {
controlPlane.rotateRunIdentity(identity);
} catch (err) {
if (err instanceof Error && err.message.includes("requires a durable transition receipt")) {
await performWarmTransition(identity); // create receipt, then rotate
controlPlane.rotateRunIdentity(identity, attachTemplate);
} else throw err;
} Prevention
- Only call rotateRunIdentity cold on a store that never recorded a run.attach command.
- Never compact or prune transition records while run.attach command history remains.
- Route all identity changes for attached runs through the warm transition flow.
When it happens
Trigger: Calling rotateRunIdentity(identity) on a store that has recorded a run.attach command but whose warmTransition/completedWarmTransition entries are absent (e.g. they were compacted, or the transition was never performed and the caller attempts a cold rotation over an attached run).
Common situations: Store compaction or reset dropped transition records while command history remained; calling cold-start rotation on a resumed run; migration of a store from a version that did not persist transitions.
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
- Completed warm transition template conflicts with its exact
- Warm transition result is not yet authenticated.
- Warm run transition target conflicts with its durable receip
- Warm run transition template conflicts with its exact comman
- Durable PRP run identity rotation is invalid.
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/746c5318de593e4f.
Report an issue: GitHub.