{"record":{"id":"6bb234b09a33ebaf","repo":"paperclipai/paperclip","slug":"paperclip-cloud-runtime-identity-jwks-is-invalid","errorCode":null,"errorMessage":"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid","messagePattern":"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/cloud-runtime-identity.ts","lineNumber":241,"sourceCode":"  try {\n    parsed = JSON.parse(Buffer.from(part, \"base64url\").toString(\"utf8\"));\n  } catch {\n    throw new Error(`Cloud runtime identity has an invalid ${label}`);\n  }\n  if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n    throw new Error(`Cloud runtime identity has an invalid ${label}`);\n  }\n  return parsed as Record<string, unknown>;\n}\n\nfunction publicKeyForKid(env: NodeJS.ProcessEnv, kid: string) {\n  const raw = nonEmpty(env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS);\n  if (!raw) throw new Error(\"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is not configured\");\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    throw new Error(\"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid\");\n  }\n  const keys = parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n    ? (parsed as { keys?: unknown }).keys\n    : undefined;\n  if (!Array.isArray(keys)) throw new Error(\"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid\");\n  const matches = keys.filter((candidate): candidate is JsonWebKey & { kid: string } => {\n    if (!candidate || typeof candidate !== \"object\" || Array.isArray(candidate)) return false;\n    const key = candidate as JsonWebKey & { kid?: unknown };\n    return key.kid === kid;\n  });\n  if (matches.length !== 1) throw new Error(\"Cloud runtime identity uses an unknown signing key\");\n  const jwk = matches[0];\n  if (jwk.kty !== \"OKP\" || jwk.crv !== \"Ed25519\" || jwk.use !== \"sig\" || jwk.alg !== \"EdDSA\" || !jwk.x || jwk.d) {\n    throw new Error(\"Cloud runtime identity signing key is invalid\");\n  }\n  return createPublicKey({ key: jwk, format: \"jwk\" });\n}\n","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/cloud-runtime-identity.ts#L223-L259","documentation":"Thrown by publicKeyForKid when PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is set but its value is not parseable as JSON, or (same message reused at line 246) when the parsed JSON has no 'keys' array. The env var must contain a JWKS document: a JSON object like {\"keys\":[...]}.","triggerScenarios":"JSON.parse of the env var value throws (line 241), or the parsed value is not an object, is an array, or its 'keys' property is not an array (line 246). Both raise the identical message.","commonSituations":"The JWKS was pasted into the env var with shell quoting damage (escaped quotes consumed, JSON truncated); a bare key object {\"kty\":...} was configured instead of a wrapping JWKS {\"keys\":[...]}; the value was stored as base64 instead of raw JSON; YAML/k8s stringification mangled newlines or quotes.","solutions":["Log or echo the env var value and run it through JSON.parse locally to see the parse error","Ensure the value is a complete JWKS document with a top-level 'keys' array: {\"keys\":[{\"kty\":\"OKP\",...}]}","Fix shell/manifest quoting so quotes survive (single-quote the value in shell, use a k8s secret not an inline literal)","Confirm the control-plane JWKS endpoint output is copied verbatim, not re-encoded"],"exampleFix":"// before: bare key, no keys wrapper\nPAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS='{\"kty\":\"OKP\",\"crv\":\"Ed25519\",...}'\n// after: proper JWKS\nPAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS='{\"keys\":[{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"use\":\"sig\",\"alg\":\"EdDSA\",\"kid\":\"...\",\"x\":\"...\"}]}'","handlingStrategy":"validation","validationCode":"function jwksIsParseable(raw: string | undefined): boolean {\n  if (!raw) return false;\n  try {\n    const parsed = JSON.parse(raw);\n    return typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed) && Array.isArray((parsed as { keys?: unknown }).keys);\n  } catch { return false; }\n}\n// run at startup: if (!jwksIsParseable(process.env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS)) failFast();","typeGuard":null,"tryCatchPattern":"try {\n  verifyCloudRuntimeIdentityAssertion({ compactJws: assertion, expectedPreviousOrigin: prev });\n} catch (e) {\n  if (String((e as Error).message).includes(\"JWKS is invalid\")) {\n    logger.error(\"Configured JWKS is not valid JSON with a keys array; check quoting/encoding of the env var\", { error: e });\n  } else throw e;\n}","preventionTips":["Validate the JWKS parses at process startup, not lazily at first verification","When storing JSON in shell/env, single-quote the value to protect double quotes","Store the JWKS in a secret manager and inject it as one raw JSON string, not via shell interpolation","Never base64-wrap the JWKS in the env var; paste raw JSON"],"tags":["config","json","env-var","jwks"],"backgroundTag":"invalid-json-response","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}