{"record":{"id":"0c153cdff4e68d6d","repo":"paperclipai/paperclip","slug":"cloud-runtime-identity-destination-is-invalid","errorCode":null,"errorMessage":"Cloud runtime identity destination is invalid","messagePattern":"Cloud runtime identity destination is invalid","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/cloud-runtime-identity.ts","lineNumber":340,"sourceCode":"  const env = input.env ?? process.env;\n  const claims = verifyClaims({ compactJws: input.compactJws, env, now: input.now ?? new Date() });\n  const configuredStackId = nonEmpty(env.PAPERCLIP_CLOUD_STACK_ID);\n  if (!configuredStackId || claims.sub !== configuredStackId) {\n    throw new Error(\"Cloud runtime identity stack does not match this instance\");\n  }\n  const previousOrigin = exactHttpsOrigin(claims.previousOrigin);\n  const canonicalOrigin = exactHttpsOrigin(claims.canonicalOrigin);\n  if (!previousOrigin || !canonicalOrigin || previousOrigin !== input.expectedPreviousOrigin) {\n    throw new Error(\"Cloud runtime identity previous or canonical origin is invalid\");\n  }\n  if (\n    !STACK_SLUG_PATTERN.test(claims.stackSlug)\n    || new URL(canonicalOrigin).hostname.split(\".\")[0] !== claims.stackSlug\n    || claims.claimId.length > 256\n    || claims.claimId.trim() !== claims.claimId\n    || !claims.claimId\n  ) {\n    throw new Error(\"Cloud runtime identity destination is invalid\");\n  }\n  return claims;\n}\n\nfunction assertionsEqual(row: PersistedRuntimeIdentity, claims: RuntimeIdentityClaims): boolean {\n  const left = Buffer.from(JSON.stringify([\n    row.stackId,\n    row.claimId,\n    row.previousOrigin,\n    row.canonicalOrigin,\n    row.stackSlug,\n  ]));\n  const right = Buffer.from(JSON.stringify([\n    claims.sub,\n    claims.claimId,\n    claims.previousOrigin,\n    claims.canonicalOrigin,\n    claims.stackSlug,","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/cloud-runtime-identity.ts#L322-L358","documentation":"After origin checks, verifyCloudRuntimeIdentityAssertion validates the destination identity: stackSlug must match STACK_SLUG_PATTERN, the first hostname label of canonicalOrigin must equal stackSlug, and claimId must be non-empty, trimmed, and at most 256 characters. It throws when the assertion's destination routing data is internally inconsistent.","triggerScenarios":"Assertion where stackSlug fails the slug regex, canonicalOrigin hostname's first label differs from stackSlug (e.g. origin points at a different stack's hostname), or claimId is empty, has whitespace, or exceeds 256 chars.","commonSituations":"Minting service generated a slug that doesn't match the DNS name of the canonical origin; assertion fields edited/truncated in transit; claimId generator produced an empty or padded string; cross-stack assertion copy where slug and hostname disagree.","solutions":["Re-mint the assertion ensuring canonicalOrigin's first DNS label equals stackSlug and stackSlug passes the slug pattern","Regenerate claimId as a trimmed non-empty string <= 256 chars","Compare the minted payload fields against this instance's real hostname before sending","Check the minter's slug/hostname construction code for drift"],"exampleFix":"// before\nstackSlug: \"my-stack\", canonicalOrigin: \"https://other-stack.example.com\"\n// after\nstackSlug: \"my-stack\", canonicalOrigin: \"https://my-stack.example.com\"","handlingStrategy":"validation","validationCode":"const slugOk = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(claims.stackSlug);\nconst hostOk = new URL(claims.canonicalOrigin).hostname.split('.')[0] === claims.stackSlug;\nconst idOk = claims.claimId && claims.claimId.length <= 256 && claims.claimId.trim() === claims.claimId;\nif (!slugOk || !hostOk || !idOk) throw new Error('destination claims invalid before apply');","typeGuard":"function validDestination(c: {stackSlug:string;canonicalOrigin:string;claimId:string}): boolean {\n  try {\n    return c.claimId.length > 0 && c.claimId.length <= 256 && c.claimId.trim() === c.claimId &&\n      new URL(c.canonicalOrigin).hostname.split('.')[0] === c.stackSlug;\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  await applyCloudRuntimeIdentityAssertion({ db, compactJws: jws });\n} catch (e) {\n  if (e.message.includes('destination is invalid')) {\n    const claims = decodeJwtPayload(jws);\n    logger.error({ stackSlug: claims.stackSlug, canonicalOrigin: claims.canonicalOrigin, claimIdLen: claims.claimId?.length }, 'destination claims inconsistent');\n  }\n  throw e;\n}","preventionTips":["Derive stackSlug from the canonical hostname's first label at mint time so they can never diverge","Generate claimId with a fixed trimmed format (e.g. uuid) and cap length","Add a minter-side pre-send validation mirroring the verifier's checks","Don't hand-edit or truncate assertion JSON in transit"],"tags":["auth","validation","cloud"],"backgroundTag":"schema-validation-failed","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}