paperclipai/paperclip · critical

Persisted Cloud runtime identity does not match PAPERCLIP_CL

Error message

Persisted Cloud runtime identity does not match PAPERCLIP_CLOUD_STACK_ID

What it means

Paperclip's Cloud runtime identity is durably persisted in the instance_settings table on first claim. On every startup, initializeCloudRuntimeIdentity reads that persisted row and calls assertPersistedIdentityMatchesStack, which requires PAPERCLIP_CLOUD_STACK_ID in the environment to be present and exactly equal to the stackId stored in the durable claim. This error means the process is running with a stack id that does not match the one this database instance was claimed by, so the runtime refuses to start with a mismatched Cloud identity. It is a fail-fast guard against pointing a claimed instance at the wrong Cloud stack.

Source

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

  const existingCandidates = (() => {
    try {
      const parsed = JSON.parse(env.PAPERCLIP_RUNTIME_API_CANDIDATES_JSON ?? "[]");
      return Array.isArray(parsed) ? parsed.filter((value): value is string => typeof value === "string") : [];
    } catch {
      return [];
    }
  })();
  env.PAPERCLIP_RUNTIME_API_CANDIDATES_JSON = JSON.stringify([
    identity.canonicalOrigin,
    ...existingCandidates.filter((candidate) => candidate !== identity.canonicalOrigin),
  ]);
}

function assertPersistedIdentityMatchesStack(row: PersistedRuntimeIdentity, env: NodeJS.ProcessEnv) {
  const configuredStackId = nonEmpty(env.PAPERCLIP_CLOUD_STACK_ID);
  if (!configuredStackId || configuredStackId !== row.stackId) {
    throw new Error("Persisted Cloud runtime identity does not match PAPERCLIP_CLOUD_STACK_ID");
  }
  if (!exactHttpsOrigin(row.previousOrigin) || !exactHttpsOrigin(row.canonicalOrigin)) {
    throw new Error("Persisted Cloud runtime identity contains an invalid origin");
  }
  if (!STACK_SLUG_PATTERN.test(row.stackSlug) || new URL(row.canonicalOrigin).hostname.split(".")[0] !== row.stackSlug) {
    throw new Error("Persisted Cloud runtime identity contains an invalid stack slug");
  }
}

/** Load the durable claim before auth, routes, and child-runtime configuration. */
export async function initializeCloudRuntimeIdentity(
  db: Db,
  env: NodeJS.ProcessEnv = process.env,
): Promise<CloudRuntimeIdentitySnapshot | null> {
  startupOrigin = configuredStartupOrigin(env);
  // Self-hosted servers have no Cloud stack identity to restore. Avoid touching
  // the singleton table on that path; besides keeping the feature inert, this
  // preserves lightweight startup/test database seams that intentionally do

View on GitHub (pinned to 01ad858492)

Solutions

  1. Set PAPERCLIP_CLOUD_STACK_ID to the exact stackId recorded in the persisted claim (inspect the instanceSettings row with singletonKey 'cloud-runtime-identity/v1' and compare its general.stackId).
  2. If the instance genuinely belongs to a new stack, provision a fresh database for it rather than reusing the claimed database, because the durable claim is single-assignment.
  3. If the env var is simply missing at this startup, restore it from the deployment secret/config that originally supplied it.
  4. If the database row is stale (e.g. an abandoned stack's claim), delete the 'cloud-runtime-identity/v1' instance_settings row only if you are certain the instance was never claimed by an active stack, then restart.

Example fix

// before (deployment env)
PAPERCLIP_CLOUD_STACK_ID=stack-old-id
// after (matches the persisted claim's stackId)
PAPERCLIP_CLOUD_STACK_ID=stack-current-id
Defensive patterns

Strategy: validation

Validate before calling

// before startup / deploy
const configured = process.env.PAPERCLIP_CLOUD_STACK_ID?.trim();
if (!configured) throw new Error("PAPERCLIP_CLOUD_STACK_ID must be set in Cloud mode");
// compare against the durable claim
const row = await db.select().from(instanceSettings)
  .where(eq(instanceSettings.singletonKey, "cloud-runtime-identity/v1")).limit(1);
if (row[0] && row[0].general.stackId !== configured) {
  throw new Error(`Stack id mismatch: env=${configured} persisted=${row[0].general.stackId}`);
}

Type guard

function hasMatchingStackId(env: NodeJS.ProcessEnv, row: { stackId: string } | null): boolean {
  const configured = env.PAPERCLIP_CLOUD_STACK_ID?.trim();
  return typeof configured === "string" && configured.length > 0 && row !== null && configured === row.stackId;
}

Prevention

When it happens

Trigger: initializeCloudRuntimeIdentity is called at startup with PAPERCLIP_CLOUD_STACK_ID set (Cloud mode), a persisted identity row exists, and either (a) PAPERCLIP_CLOUD_STACK_ID is unset/empty/whitespace at this startup even though a claim exists, or (b) its trimmed value differs from row.stackId stored in the durable claim.

Common situations: Operators redeploy the instance pointing at the same database but with a wrong or missing PAPERCLIP_CLOUD_STACK_ID env var; an environment/config change (e.g. a stack was recreated with a new id) reuses the old database; a copy of a production database is restored into a different Cloud stack; the env var is injected with different casing or a stale value by the deployment tooling.

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@01ad858492 (2026-09-10). Data as JSON: /api/errors/6896c0bd4af0ef27. Report an issue: GitHub.