paperclipai/paperclip · error
Cloud runtime identity has an invalid ${label}
Error message
Cloud runtime identity has an invalid ${label} What it means
decodeJsonPart decodes one base64url-delimited part of the Cloud runtime identity compact JWS (protected header or payload). It throws this error when the part contains characters outside [A-Za-z0-9_-] (i.e. is not valid base64url), when base64url decoding plus JSON.parse fails, or — as here at line 221 — before decoding, when the pre-parse charset check fails. The label in the message is 'protected header' or 'payload' depending on the caller.
Source
Thrown at server/src/services/cloud-runtime-identity.ts:221
if (env === process.env && currentIdentity) return currentIdentity.canonicalOrigin;
const candidate = nonEmpty(env.PAPERCLIP_PUBLIC_URL)
?? nonEmpty(env.PAPERCLIP_AUTH_PUBLIC_BASE_URL)
?? nonEmpty(env.PAPERCLIP_API_URL);
if (!candidate) return null;
try {
return new URL(candidate).origin;
} catch {
return null;
}
}
/** The asserted Cloud origin only. Callers retain their non-Cloud precedence. */
export function runtimeCanonicalOrigin(): string | null {
return currentIdentity?.canonicalOrigin ?? null;
}
function decodeJsonPart(part: string, label: string): Record<string, unknown> {
if (!/^[A-Za-z0-9_-]+$/.test(part)) throw new Error(`Cloud runtime identity has an invalid ${label}`);
let parsed: unknown;
try {
parsed = JSON.parse(Buffer.from(part, "base64url").toString("utf8"));
} catch {
throw new Error(`Cloud runtime identity has an invalid ${label}`);
}
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error(`Cloud runtime identity has an invalid ${label}`);
}
return parsed as Record<string, unknown>;
}
function publicKeyForKid(env: NodeJS.ProcessEnv, kid: string) {
const raw = nonEmpty(env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS);
if (!raw) throw new Error("PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is not configured");
let parsed: unknown;
try {
parsed = JSON.parse(raw);View on GitHub (pinned to 01ad858492)
Solutions
- Regenerate or re-fetch the runtime identity assertion from the Paperclip Cloud control plane instead of reusing a stored copy; use the token exactly as issued.
- Check that the string was not damaged in transit: shell-quote it, avoid line wrapping in YAML/env files, and confirm no truncation when copying.
- If you must construct the JWS yourself, base64url-encode (RFC 4648 §5, no padding) the JSON header and payload.
- Log/inspect the failing part and confirm it matches ^[A-Za-z0-9_-]+$ and JSON-decodes after base64url decoding before calling the API.
Example fix
// before (standard base64 with padding)
const part = Buffer.from(json).toString("base64"); // e.g. "eyJ...+g=="
// after (base64url, no padding)
const part = Buffer.from(json).toString("base64url"); Defensive patterns
Strategy: validation
Validate before calling
function isBase64UrlPart(part: string): boolean {
return /^[A-Za-z0-9_-]+$/.test(part) &&
(() => { try { JSON.parse(Buffer.from(part, "base64url").toString("utf8")); return true; } catch { return false; } })();
}
// before sending the assertion to the API:
const [h, p, s] = compactJws.split(".");
if (!h || !p || !s || !isBase64UrlPart(h) || !isBase64UrlPart(p)) {
throw new Error("assertion is not a well-formed base64url compact JWS");
} Type guard
function isWellFormedJws(token: string): boolean {
const parts = token.split(".");
if (parts.length !== 3 || parts.some((p) => !p)) return false;
return parts.every((p) => /^[A-Za-z0-9_-]+$/.test(p));
} Try / catch
try {
verifyCloudRuntimeIdentityAssertion({ compactJws, expectedPreviousOrigin });
} catch (err) {
if (err instanceof Error && err.message.includes("invalid protected header")) {
// token damaged or wrongly encoded — re-fetch a fresh assertion from the control plane
}
throw err;
} Prevention
- Never re-encode a base64url token to standard base64 (no '+', '/', or '=')
- Quote the token in shell/YAML/env files to prevent mangling and line wrapping
- Copy tokens verbatim from the Cloud control plane; avoid truncation in logs or ticket text
- Round-trip decode the header/payload locally before submitting
When it happens
Trigger: verifyCloudRuntimeIdentityAssertion (via verifyClaims) is given a compactJws whose header or payload segment contains invalid base64url characters (e.g. '+' or '/' or '='), is not valid base64url encoding, or decodes to bytes that are not valid JSON.
Common situations: A caller passes a standard base64 JWT (with '+', '/', '=') instead of the base64url form; the assertion string was truncated or corrupted in transit, storage, or a shell command (quoting/line-wrap damage); someone pasted a URL-encoded or padded token; a test fixture was hand-crafted with a malformed segment.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- "tool" is required and must be a string
- "runContext" is required and must be an object
- "runContext" must include agentId, runId, companyId, and pro
- ${prefix}: the capability must be an object.
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/119051881f1888e3.
Report an issue: GitHub.