paperclipai/paperclip · error
Completed warm transition template conflicts with its exact…
Error message
Completed warm transition template conflicts with its exact command.
What it means
rotateRunIdentity() was called on a control plane whose warm transition already completed durably (completedWarmTransition is set). For an idempotent replay the caller re-passed a runAttachTemplate whose canonical JSON differs from the template recorded in the completed transition's command payload (after stripping the paperclipNextAuthority boundary field). The library fails closed because the replay does not match the exact original command.
Solutions
- Do not pass runAttachTemplate on idempotent replays of a completed warm transition; call rotateRunIdentity(identity) with the template omitted.
- Compare your template against the completed transition's command payload (minus paperclipNextAuthority) and reuse the exact original object.
- If the template genuinely must change, treat it as a new rotation: reset the transition state per your recovery procedure instead of replaying the completed one.
- Log canonicalJson(runAttachTemplate) and the completed command payload to identify which field drifted before retrying.
Example fix
// before
controlPlane.rotateRunIdentity(newIdentity, { provider: { name: "claude" }, model: "opus" }); // replay after completion, template drifted
// after
controlPlane.rotateRunIdentity(newIdentity); // idempotent replay without re-asserting the template Defensive patterns
Strategy: try-catch
Validate before calling
// before replaying a completed transition
const completed = store.state.completedWarmTransition;
if (completed && runAttachTemplate !== undefined) {
const { paperclipNextAuthority: _b, ...expected } = completed.command.payload;
if (canonicalJson(runAttachTemplate) !== canonicalJson(expected)) {
runAttachTemplate = undefined; // omit on idempotent replay
}
} Try / catch
try {
controlPlane.rotateRunIdentity(identity, runAttachTemplate);
} catch (err) {
if (err instanceof Error && err.message.includes("Completed warm transition template conflicts")) {
controlPlane.rotateRunIdentity(identity); // replay without re-asserting template
} else throw err;
} Prevention
- Omit runAttachTemplate on retries of an already-completed warm transition.
- Keep the template immutable (frozen) once a transition is initiated.
- Strip volatile fields from templates before passing them to rotation.
When it happens
Trigger: Calling rotateRunIdentity(identity, runAttachTemplate) a second time with the same identity after the transition completed, but with a modified runAttachTemplate (different keys, values, ordering-insensitive but content-different) than the one used in the original, already-completed transition.
Common situations: Caller code recomputes the attach template from drifted config (e.g. provider settings changed between retries); a serialization layer adds or drops fields; a retry path passes an updated template while the transition already succeeded on the first attempt.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Warm run transition template conflicts with its exact…
- Warm run transition target conflicts with its durable…
- delivery_unknown
- Durable PRP run attachment template conflicts.
- Durable PRP run identity rotation is invalid.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-10).
Data as JSON: /api/errors/23a3109c2162d0b3.
Report an issue: GitHub.
Appendix: source
Thrown at packages/paperclip-runner/src/control-plane/durable-prp-control-plane.ts:1647
rotateRunIdentity(
identity: DurableRecoveryIdentity,
runAttachTemplate?: Record<string, unknown>,
): void {
if (this.#protocolIntegrityError !== null)
throw this.#protocolIntegrityError;
const completed = this.#store.state.completedWarmTransition;
if (
completed &&
canonicalJson(identity) === canonicalJson(this.#identity) &&
canonicalJson(identity) === canonicalJson(completed.receipt.newIdentity)
) {
const { paperclipNextAuthority: _boundary, ...template } =
completed.command.payload;
if (
runAttachTemplate !== undefined &&
canonicalJson(runAttachTemplate) !== canonicalJson(template)
) {
throw new Error(
"Completed warm transition template conflicts with its exact command.",
);
}
return;
}
const transition = this.#store.state.warmTransition;
if (transition) {
if (transition.phase === "awaiting_result")
throw new Error("Warm transition result is not yet authenticated.");
if (
canonicalJson(identity) !==
canonicalJson(transition.receipt.newIdentity)
) {
throw new Error(
"Warm run transition target conflicts with its durable receipt.",
);
}
// The new authenticated peer, not an attach-result observer, owns theView on GitHub (pinned to 3f1d897a7c)