paperclipai/paperclip · error

Cloud control assertion stack does not match this instance

Error message

Cloud control assertion stack does not match this instance

What it means

verifyCloudControlAssertion binds every assertion to a specific cloud stack. This error is thrown when PAPERCLIP_CLOUD_STACK_ID is not configured on this instance, or when the payload's sub claim does not equal the configured stack id. It prevents an assertion minted for one stack from controlling another.

Solutions

  1. Set PAPERCLIP_CLOUD_STACK_ID on the instance to the stack id this runtime belongs to and restart the server
  2. Ensure the assertion issuer uses that same stack id as the sub claim when minting tokens
  3. Check deployment config: confirm the client and instance point at the same environment/stack (staging vs production mix-up)
  4. Re-issue assertions after any stack rename or infra migration

Example fix

// before
# instance env (missing)
// after
PAPERCLIP_CLOUD_STACK_ID=stack_01h_example   # must equal the sub claim of minted assertions
Defensive patterns

Strategy: validation

Validate before calling

const stackId = process.env.PAPERCLIP_CLOUD_STACK_ID;
if (!stackId) throw new Error("PAPERCLIP_CLOUD_STACK_ID must be set before accepting control assertions");
if (minted.sub !== stackId) throw new Error(`assertion sub ${minted.sub} does not match stack ${stackId}`);

Try / catch

try {
  return verifyCloudControlAssertion({ compactJws: token, expectedAction });
} catch (e) {
  if (e.message === "Cloud control assertion stack does not match this instance") {
    return respond(403, "assertion is for a different stack"); // cross-environment token: do not retry here
  }
  throw e;
}

Prevention

When it happens

Trigger: A token whose sub names a different stack is sent to this instance; PAPERCLIP_CLOUD_STACK_ID is unset/empty on the verifying instance (so configuredStackId is undefined and the guard fails); the client was configured with the wrong stack id when minting assertions.

Common situations: Environment variables not propagated to a replica or canary deployment; a control-plane client pointing at the wrong environment (staging token against production instance); stack renamed/recreated without re-issuing assertions; copy-pasted env config between deployments.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/3bf5756225177d26. Report an issue: GitHub.

Appendix: source

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

  const payload = decodeJsonPart(encodedPayload, "payload");
  const configuredStackId = nonEmpty(env.PAPERCLIP_CLOUD_STACK_ID);
  const nowSeconds = Math.floor(now.getTime() / 1000);
  if (
    payload.v !== 1
    || payload.iss !== CLOUD_RUNTIME_IDENTITY_ISSUER
    || payload.aud !== CLOUD_CONTROL_AUDIENCE
    || typeof payload.sub !== "string"
    || typeof payload.action !== "string"
    || typeof payload.requestId !== "string"
    || typeof payload.iat !== "number"
    || !Number.isInteger(payload.iat)
    || typeof payload.exp !== "number"
    || !Number.isInteger(payload.exp)
  ) {
    throw new Error("Cloud control claims are incomplete");
  }
  if (!configuredStackId || payload.sub !== configuredStackId) {
    throw new Error("Cloud control assertion stack does not match this instance");
  }
  if (
    !(CLOUD_CONTROL_ACTIONS as readonly string[]).includes(payload.action)
    || payload.action !== input.expectedAction
  ) {
    throw new Error("Cloud control assertion does not authorize this action");
  }
  if (
    !payload.requestId
    || payload.requestId.trim() !== payload.requestId
    || payload.requestId.length > 256
  ) {
    throw new Error("Cloud control assertion request id is invalid");
  }
  if (
    payload.exp <= nowSeconds
    || payload.iat > nowSeconds + MAX_CLOCK_SKEW_SECONDS
    || payload.exp <= payload.iat

View on GitHub (pinned to 3f1d897a7c)