paperclipai/paperclip · error

Cloud runtime identity destination is invalid

Error message

Cloud runtime identity destination is invalid

What it means

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.

Source

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

  const env = input.env ?? process.env;
  const claims = verifyClaims({ compactJws: input.compactJws, env, now: input.now ?? new Date() });
  const configuredStackId = nonEmpty(env.PAPERCLIP_CLOUD_STACK_ID);
  if (!configuredStackId || claims.sub !== configuredStackId) {
    throw new Error("Cloud runtime identity stack does not match this instance");
  }
  const previousOrigin = exactHttpsOrigin(claims.previousOrigin);
  const canonicalOrigin = exactHttpsOrigin(claims.canonicalOrigin);
  if (!previousOrigin || !canonicalOrigin || previousOrigin !== input.expectedPreviousOrigin) {
    throw new Error("Cloud runtime identity previous or canonical origin is invalid");
  }
  if (
    !STACK_SLUG_PATTERN.test(claims.stackSlug)
    || new URL(canonicalOrigin).hostname.split(".")[0] !== claims.stackSlug
    || claims.claimId.length > 256
    || claims.claimId.trim() !== claims.claimId
    || !claims.claimId
  ) {
    throw new Error("Cloud runtime identity destination is invalid");
  }
  return claims;
}

function assertionsEqual(row: PersistedRuntimeIdentity, claims: RuntimeIdentityClaims): boolean {
  const left = Buffer.from(JSON.stringify([
    row.stackId,
    row.claimId,
    row.previousOrigin,
    row.canonicalOrigin,
    row.stackSlug,
  ]));
  const right = Buffer.from(JSON.stringify([
    claims.sub,
    claims.claimId,
    claims.previousOrigin,
    claims.canonicalOrigin,
    claims.stackSlug,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Re-mint the assertion ensuring canonicalOrigin's first DNS label equals stackSlug and stackSlug passes the slug pattern
  2. Regenerate claimId as a trimmed non-empty string <= 256 chars
  3. Compare the minted payload fields against this instance's real hostname before sending
  4. Check the minter's slug/hostname construction code for drift

Example fix

// before
stackSlug: "my-stack", canonicalOrigin: "https://other-stack.example.com"
// after
stackSlug: "my-stack", canonicalOrigin: "https://my-stack.example.com"
Defensive patterns

Strategy: validation

Validate before calling

const slugOk = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(claims.stackSlug);
const hostOk = new URL(claims.canonicalOrigin).hostname.split('.')[0] === claims.stackSlug;
const idOk = claims.claimId && claims.claimId.length <= 256 && claims.claimId.trim() === claims.claimId;
if (!slugOk || !hostOk || !idOk) throw new Error('destination claims invalid before apply');

Type guard

function validDestination(c: {stackSlug:string;canonicalOrigin:string;claimId:string}): boolean {
  try {
    return c.claimId.length > 0 && c.claimId.length <= 256 && c.claimId.trim() === c.claimId &&
      new URL(c.canonicalOrigin).hostname.split('.')[0] === c.stackSlug;
  } catch { return false; }
}

Try / catch

try {
  await applyCloudRuntimeIdentityAssertion({ db, compactJws: jws });
} catch (e) {
  if (e.message.includes('destination is invalid')) {
    const claims = decodeJwtPayload(jws);
    logger.error({ stackSlug: claims.stackSlug, canonicalOrigin: claims.canonicalOrigin, claimIdLen: claims.claimId?.length }, 'destination claims inconsistent');
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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