paperclipai/paperclip · error

Cloud runtime identity assertion is expired or has an invali

Error message

Cloud runtime identity assertion is expired or has an invalid lifetime

What it means

verifyClaims in cloud-runtime-identity.ts validates the lifetime window of a signed runtime identity JWS assertion before accepting the runtime identity claims. It throws when the assertion is expired, stamped in the future beyond allowed clock skew, or has an exp/iat span that is empty or exceeds MAX_ASSERTION_LIFETIME_SECONDS. This prevents replay of stale or over-long-lived identity assertions when claiming a cloud runtime instance.

Source

Thrown at server/src/services/cloud-runtime-identity.ts:310

    || typeof payload.sub !== "string"
    || typeof payload.claimId !== "string"
    || typeof payload.previousOrigin !== "string"
    || typeof payload.canonicalOrigin !== "string"
    || typeof payload.stackSlug !== "string"
    || typeof payload.iat !== "number"
    || !Number.isInteger(payload.iat)
    || typeof payload.exp !== "number"
    || !Number.isInteger(payload.exp)
  ) {
    throw new Error("Cloud runtime identity claims are incomplete");
  }
  if (
    payload.exp <= nowSeconds
    || payload.iat > nowSeconds + MAX_CLOCK_SKEW_SECONDS
    || payload.exp <= payload.iat
    || payload.exp - payload.iat > MAX_ASSERTION_LIFETIME_SECONDS
  ) {
    throw new Error("Cloud runtime identity assertion is expired or has an invalid lifetime");
  }
  return payload as RuntimeIdentityClaims;
}

/** Verify that an assertion is signed for this exact, still-unclaimed instance. */
export function verifyCloudRuntimeIdentityAssertion(input: {
  compactJws: string;
  env?: NodeJS.ProcessEnv;
  now?: Date;
  expectedPreviousOrigin: string | null;
}): RuntimeIdentityClaims {
  const env = input.env ?? process.env;
  const claims = verifyClaims({ compactJws: input.compactJws, env, now: input.now ?? new Date() });
  const configuredStackId = nonEmpty(env.PAPERCLIP_CLOUD_STACK_ID);
  if (!configuredStackId || claims.sub !== configuredStackId) {
    throw new Error("Cloud runtime identity stack does not match this instance");
  }
  const previousOrigin = exactHttpsOrigin(claims.previousOrigin);

View on GitHub (pinned to 01ad858492)

Solutions

  1. Re-mint a fresh runtime identity assertion and retry the claim immediately
  2. Synchronize clock (NTP) on both the minting service and the instance
  3. Fix the minter to set iat < exp within MAX_ASSERTION_LIFETIME_SECONDS
  4. Verify no stale assertion is being persisted/replayed across restarts

Example fix

// before
const jws = mintAssertion({ iat: now - 3600, exp: now + 3600 });
// after
const iat = Math.floor(Date.now() / 1000);
const jws = mintAssertion({ iat, exp: iat + 300 });
Defensive patterns

Strategy: validation

Validate before calling

const payload = decodeJwtPayload(jws);
const now = Math.floor(Date.now()/1000);
const ok = payload.exp > now && payload.iat <= now + 60 && payload.exp > payload.iat && (payload.exp - payload.iat) <= 600;
if (!ok) throw new Error('assertion lifetime invalid; re-mint before applying');

Type guard

function hasValidLifetime(p: {iat:number;exp:number}, nowSec:number): boolean {
  return p.exp > nowSec && p.iat <= nowSec + 60 && p.exp > p.iat && p.exp - p.iat <= 600;
}

Try / catch

try {
  await applyCloudRuntimeIdentityAssertion({ db, compactJws: jws });
} catch (e) {
  if (e.message.includes('invalid lifetime')) jws = reMintAssertion(); // re-mint and retry once
  else throw e;
}

Prevention

When it happens

Trigger: Calling verifyClaims (directly or via verifyCloudRuntimeIdentityAssertion/applyCloudRuntimeIdentityAssertion) with a compactJws whose payload.exp <= now, whose iat is more than MAX_CLOCK_SKEW_SECONDS in the future, whose exp <= iat, or whose lifetime exceeds MAX_ASSERTION_LIFETIME_SECONDS.

Common situations: Minter and verifier clocks drifted apart; an assertion was cached and reused after expiry; a minting service issued assertions with a too-long or inverted lifetime (iat after exp); an old assertion snapshot was retried after a delay.

Related errors


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