paperclipai/paperclip · error

Warm transition result is not yet authenticated.

Error message

Warm transition result is not yet authenticated.

What it means

rotateRunIdentity() was invoked while the durable warmTransition exists in phase "awaiting_result", meaning the transition handshake has been issued but its authenticated result (receipt) has not yet been written to the durable store. Identity rotation cannot proceed on an unauthenticated transition, so the call is rejected.

Source

Thrown at packages/paperclip-runner/src/control-plane/durable-prp-control-plane.ts:1638

      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 the
      // activation boundary. Keep the old credential and command replay lane.
      if (runAttachTemplate !== undefined) {
        const { paperclipNextAuthority: _boundary, ...expectedTemplate } =
          transition.command.payload;
        if (
          canonicalJson(runAttachTemplate) !== canonicalJson(expectedTemplate)
        ) {
          throw new Error(
            "Warm run transition template conflicts with its exact command.",

View on GitHub (pinned to 01ad858492)

Solutions

  1. Wait until the warm transition advances past "awaiting_result" (result authenticated durably) before calling rotateRunIdentity.
  2. Poll store state / listen for the transition-completion event instead of retrying rotation on a fixed timer.
  3. If the transition is permanently stuck in awaiting_result, use the recovery procedure (bootstrap ticket path) to clear it rather than forcing rotation.
  4. Guard the call site: only rotate when warmTransition is undefined or its phase is not "awaiting_result".

Example fix

// before
if (state.warmTransition) controlPlane.rotateRunIdentity(identity); // may hit awaiting_result
// after
if (!state.warmTransition || state.warmTransition.phase !== "awaiting_result") {
  controlPlane.rotateRunIdentity(identity);
} else {
  await waitForTransitionResult();
}
Defensive patterns

Strategy: retry

Validate before calling

const t = store.state.warmTransition;
const canRotate = !t || t.phase !== "awaiting_result";
if (!canRotate) await waitForTransitionResult();

Type guard

function canRotate(state: { warmTransition?: { phase: string } | null }): boolean {
  return !state.warmTransition || state.warmTransition.phase !== "awaiting_result";
}

Try / catch

try {
  controlPlane.rotateRunIdentity(identity);
} catch (err) {
  if (err instanceof Error && err.message.includes("not yet authenticated")) {
    await waitForTransitionResult(); // then retry once
    controlPlane.rotateRunIdentity(identity);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling rotateRunIdentity() between the moment a warm transition was initiated and the moment its result/receipt was durably committed — e.g. a runner or supervisor retries rotation immediately after triggering the transition without waiting for the run.attach acknowledgment to settle.

Common situations: Race between a transition initiator and a rotation retry loop; a crash/restart leaves the transition mid-handshake and the recovery code calls rotateRunIdentity before the peer's result lands; polling logic doesn't check transition.phase first.

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


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/ad89f61d3a5dd50e. Report an issue: GitHub.