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
- Re-mint a fresh runtime identity assertion and retry the claim immediately
- Synchronize clock (NTP) on both the minting service and the instance
- Fix the minter to set iat < exp within MAX_ASSERTION_LIFETIME_SECONDS
- 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
- Mint assertions with short lifetimes well under MAX_ASSERTION_LIFETIME_SECONDS and use immediately
- Run NTP on all minting and verifying hosts
- Never cache or persist assertions across their exp
- Unit-test the minter's iat/exp ordering
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
- invalid_cloud_runtime_identity
- ${options.errorLabel} decision predicate failed: ${detail}
- ${options.errorLabel} decision predicate exited 0 (expected
- PAPERCLIP_BRIDGE_TOKEN is required.
- No auth user has a non-empty credential account, instance-ad
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/84bf00b9b9bc7931.
Report an issue: GitHub.