paperclipai/paperclip · error
Cloud runtime identity assertion is not a compact JWS
Error message
Cloud runtime identity assertion is not a compact JWS
What it means
Thrown by verifyClaims when the assertion string is not a three-part compact JWS. The verifier splits on '.' and requires exactly 3 non-empty segments (header.payload.signature); any other shape is rejected before any decoding happens.
Source
Thrown at server/src/services/cloud-runtime-identity.ts:267
const key = candidate as JsonWebKey & { kid?: unknown };
return key.kid === kid;
});
if (matches.length !== 1) throw new Error("Cloud runtime identity uses an unknown signing key");
const jwk = matches[0];
if (jwk.kty !== "OKP" || jwk.crv !== "Ed25519" || jwk.use !== "sig" || jwk.alg !== "EdDSA" || !jwk.x || jwk.d) {
throw new Error("Cloud runtime identity signing key is invalid");
}
return createPublicKey({ key: jwk, format: "jwk" });
}
function verifyClaims(input: {
compactJws: string;
env: NodeJS.ProcessEnv;
now: Date;
}): RuntimeIdentityClaims {
const parts = input.compactJws.split(".");
if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
throw new Error("Cloud runtime identity assertion is not a compact JWS");
}
const [encodedHeader, encodedPayload, encodedSignature] = parts;
const header = decodeJsonPart(encodedHeader, "protected header");
if (
header.alg !== "EdDSA"
|| header.typ !== CLOUD_RUNTIME_IDENTITY_JWS_TYPE
|| typeof header.kid !== "string"
|| !header.kid
) {
throw new Error("Cloud runtime identity protected header is invalid");
}
const key = publicKeyForKid(input.env, header.kid);
const signature = Buffer.from(encodedSignature, "base64url");
const signingInput = Buffer.from(`${encodedHeader}.${encodedPayload}`, "ascii");
if (!verify(null, signingInput, key, signature)) {
throw new Error("Cloud runtime identity signature is invalid");
}
View on GitHub (pinned to 01ad858492)
Solutions
- Confirm you are passing the signed assertion token (header.payload.signature), not a JWKS or claims JSON
- Trim whitespace/newlines from the token before verification
- Re-issue the assertion from the Cloud control plane if it was truncated in transport or logs
- Count the dot-separated segments: there must be exactly 3, all non-empty
Example fix
// before: passing claims JSON instead of the signed assertion
verifyCloudRuntimeIdentityAssertion({ compactJws: JSON.stringify(claims), expectedPreviousOrigin: prev });
// after: pass the JWS string as issued
verifyCloudRuntimeIdentityAssertion({ compactJws: process.env.PAPERCLIP_CLOUD_IDENTITY_ASSERTION!.trim(), expectedPreviousOrigin: prev }); Defensive patterns
Strategy: validation
Validate before calling
function isCompactJws(token: unknown): token is string {
if (typeof token !== "string") return false;
const parts = token.trim().split(".");
return parts.length === 3 && parts.every(p => p.length > 0 && /^[A-Za-z0-9_-]+$/.test(p));
}
if (!isCompactJws(assertion)) throw new Error("refusing to verify: not a compact JWS"); Try / catch
try {
verifyCloudRuntimeIdentityAssertion({ compactJws: assertion, expectedPreviousOrigin: prev });
} catch (e) {
if (String((e as Error).message).includes("not a compact JWS")) {
logger.warn("Assertion missing or malformed; requesting a fresh assertion", { error: e });
assertion = await controlPlaneClient.mintIdentityAssertion(stackId);
} else throw e;
} Prevention
- Type-check inputs: only accept string assertions from a single, named config source
- Never confuse the JWKS env var with the assertion env var — name them distinctly
- Trim surrounding whitespace only; never alter token contents
- Add a smoke test verifying a freshly minted assertion passes shape validation
When it happens
Trigger: verifyCloudRuntimeIdentityAssertion receives a token that is empty, a raw JSON blob, a JWT with missing signature segment, a token containing extra dots (e.g. embedded in a larger string), or segments that are empty strings.
Common situations: Passing the wrong env var/config value (e.g. the JWKS itself) where the assertion is expected; truncated token from log copying; double-embedding the token in an envelope that adds characters; newline or whitespace included and split logic encounters an empty part.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- invalid_cloud_runtime_identity
- Cloud runtime identity has an invalid ${label}
- PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is not configured
- Cloud runtime identity protected header is invalid
- Cloud runtime identity signature is invalid
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/9e202e58b66aca22.
Report an issue: GitHub.