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

  1. Confirm you are passing the signed assertion token (header.payload.signature), not a JWKS or claims JSON
  2. Trim whitespace/newlines from the token before verification
  3. Re-issue the assertion from the Cloud control plane if it was truncated in transport or logs
  4. 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

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


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