paperclipai/paperclip · error

Cloud control assertion is not a compact JWS

Error message

Cloud control assertion is not a compact JWS

What it means

verifyCloudControlAssertion validates an EdDSA-signed compact JWS assertion used for cloud control-plane requests. The very first structural check splits the token on '.' and requires exactly three non-empty parts (header.payload.signature). This error is thrown when the input is not a three-segment, dot-delimited compact JWS at all.

Solutions

  1. Log the received assertion shape (segment count, lengths only — never the token itself) to confirm whether it has 3 dot-separated non-empty parts
  2. Fix the client/signer to emit a proper compact JWS: base64url(header).base64url(payload).base64url(signature) with EdDSA/Ed25519
  3. Check that no intermediary rewrites, trims, or wraps the Authorization header value
  4. Reject at the client before sending: validate the token format locally with the validation snippet below

Example fix

// before
await fetch(api, { headers: { authorization: `Bearer ${assertion.trim()}` } });
// after
const compact = assertion.trim();
if (compact.split(".").length !== 3 || compact.split(".").some(p => p.length === 0)) {
  throw new Error("refusing to send: assertion is not a compact JWS");
}
await fetch(api, { headers: { authorization: `Bearer ${compact}` } });
Defensive patterns

Strategy: validation

Validate before calling

function isCompactJws(token) {
  if (typeof token !== "string") return false;
  const parts = token.split(".");
  return parts.length === 3 && parts.every(p => p.length > 0);
}
if (!isCompactJws(assertion)) throw new Error("assertion must be a compact JWS before sending");

Type guard

const isCompactJws = (v: unknown): v is string =>
  typeof v === "string" && v.split(".").length === 3 && v.split(".").every(p => p.length > 0);

Try / catch

try {
  const claims = await verifyCloudControlAssertion({ compactJws: token, expectedAction });
} catch (e) {
  if (e.message === "Cloud control assertion is not a compact JWS") {
    return respond(401, "malformed assertion"); // structural problem: do not retry the same token
  }
  throw e;
}

Prevention

When it happens

Trigger: cloudControlMiddleware / verify receives an Authorization header or assertion value that is empty, whitespace, a raw JWT with fewer/more than three segments, a bearer token of another kind, a base64-encoded blob, or a JWS that was truncated or URL-mangled in transit.

Common situations: A client sends a PASETO or opaque token instead of the expected compact JWS; a proxy or reverse proxy stripped or rewrote the header; the caller URL-encoded the token introducing percent escapes; a misconfigured SDK sends its own API key instead of a cloud control assertion.

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@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/7d6cbae1052cbe76. Report an issue: GitHub.

Appendix: source

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

/**
 * Verify a Cloud control assertion for one exact action on this instance.
 * Signed with the same key set as the runtime identity assertion
 * (PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS) but under its own JWS type and
 * audience. Instances without a Cloud stack identity reject every assertion —
 * the feature is inert when self-hosted.
 */
export function verifyCloudControlAssertion(input: {
  compactJws: string;
  expectedAction: CloudControlAction;
  env?: NodeJS.ProcessEnv;
  now?: Date;
}): CloudControlClaims {
  const env = input.env ?? process.env;
  const now = input.now ?? new Date();
  const parts = input.compactJws.split(".");
  if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
    throw new Error("Cloud control assertion is not a compact JWS");
  }
  const [encodedHeader, encodedPayload, encodedSignature] = parts;
  const header = decodeJsonPart(encodedHeader, "protected header");
  if (
    header.alg !== "EdDSA"
    || header.typ !== CLOUD_CONTROL_JWS_TYPE
    || typeof header.kid !== "string"
    || !header.kid
  ) {
    throw new Error("Cloud control protected header is invalid");
  }
  const key = publicKeyForKid(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 control signature is invalid");
  }

View on GitHub (pinned to 3f1d897a7c)