{"record":{"id":"91c4e89386c2a123","repo":"paperclipai/paperclip","slug":"persisted-cloud-runtime-identity-contains-an-inval","errorCode":null,"errorMessage":"Persisted Cloud runtime identity contains an invalid origin","messagePattern":"Persisted Cloud runtime identity contains an invalid origin","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/src/services/cloud-runtime-identity.ts","lineNumber":163,"sourceCode":"      const parsed = JSON.parse(env.PAPERCLIP_RUNTIME_API_CANDIDATES_JSON ?? \"[]\");\n      return Array.isArray(parsed) ? parsed.filter((value): value is string => typeof value === \"string\") : [];\n    } catch {\n      return [];\n    }\n  })();\n  env.PAPERCLIP_RUNTIME_API_CANDIDATES_JSON = JSON.stringify([\n    identity.canonicalOrigin,\n    ...existingCandidates.filter((candidate) => candidate !== identity.canonicalOrigin),\n  ]);\n}\n\nfunction assertPersistedIdentityMatchesStack(row: PersistedRuntimeIdentity, env: NodeJS.ProcessEnv) {\n  const configuredStackId = nonEmpty(env.PAPERCLIP_CLOUD_STACK_ID);\n  if (!configuredStackId || configuredStackId !== row.stackId) {\n    throw new Error(\"Persisted Cloud runtime identity does not match PAPERCLIP_CLOUD_STACK_ID\");\n  }\n  if (!exactHttpsOrigin(row.previousOrigin) || !exactHttpsOrigin(row.canonicalOrigin)) {\n    throw new Error(\"Persisted Cloud runtime identity contains an invalid origin\");\n  }\n  if (!STACK_SLUG_PATTERN.test(row.stackSlug) || new URL(row.canonicalOrigin).hostname.split(\".\")[0] !== row.stackSlug) {\n    throw new Error(\"Persisted Cloud runtime identity contains an invalid stack slug\");\n  }\n}\n\n/** Load the durable claim before auth, routes, and child-runtime configuration. */\nexport async function initializeCloudRuntimeIdentity(\n  db: Db,\n  env: NodeJS.ProcessEnv = process.env,\n): Promise<CloudRuntimeIdentitySnapshot | null> {\n  startupOrigin = configuredStartupOrigin(env);\n  // Self-hosted servers have no Cloud stack identity to restore. Avoid touching\n  // the singleton table on that path; besides keeping the feature inert, this\n  // preserves lightweight startup/test database seams that intentionally do\n  // not construct a database client.\n  if (!nonEmpty(env.PAPERCLIP_CLOUD_STACK_ID)) {\n    initialized = true;","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/cloud-runtime-identity.ts#L145-L181","documentation":"After confirming the stack id matches, assertPersistedIdentityMatchesStack validates that both previousOrigin and canonicalOrigin stored in the durable Cloud runtime identity row are exact HTTPS origins: absolute https URLs, no credentials, no path/query/hash, exactly equal to their parsed origin, and at most 2048 chars. If either stored origin fails exactHttpsOrigin, startup throws this error. It prevents a corrupted or tampered persisted identity from driving later URL/env derivation.","triggerScenarios":"initializeCloudRuntimeIdentity loads a persisted identity row whose previousOrigin or canonicalOrigin fails exactHttpsOrigin: not parseable as a URL, not https scheme, contains username/password, has a path other than '/', has query string or hash, includes a trailing slash or port mismatch so value !== parsed.origin, is empty, or exceeds 2048 characters.","commonSituations":"The instance_settings row was hand-edited or migrated incorrectly (e.g. stored 'https://example.com/' with trailing slash, or an http:// origin); a partial write or data import corrupted the JSON; an operator set a non-HTTPS internal URL during a manual claim; restoring a DB dump from an environment with differently shaped origins.","solutions":["Inspect the instanceSettings row with singletonKey 'cloud-runtime-identity/v1' and correct previousOrigin/canonicalOrigin to exact bare https origins (e.g. 'https://stack.example.com' — no trailing slash, path, query, or credentials).","If the row is unrecoverable or was written by a failed/aborted claim, remove the row and re-run the one-time identity assertion (applyCloudRuntimeIdentityAssertion) to re-claim cleanly.","Verify no migration or import step lowercased/normalized the URL with a trailing slash or rewrote https to http."],"exampleFix":"// persisted row in instance_settings.general\n// before\ncanonicalOrigin: \"https://my-stack.example.com/\"\n// after\n\"canonicalOrigin\": \"https://my-stack.example.com\"","handlingStrategy":"validation","validationCode":"function isExactHttpsOrigin(value: unknown): boolean {\n  if (typeof value !== \"string\" || !value || value.length > 2048) return false;\n  try {\n    const u = new URL(value);\n    return u.protocol === \"https:\" && !u.username && !u.password &&\n      u.pathname === \"/\" && !u.search && !u.hash && u.origin === value;\n  } catch { return false; }\n}\n// assert before writing/claiming:\nif (!isExactHttpsOrigin(previousOrigin) || !isExactHttpsOrigin(canonicalOrigin)) {\n  throw new Error(\"origins must be exact bare https origins, e.g. https://stack.example.com\");\n}","typeGuard":"function isExactHttpsOrigin(value: unknown): value is string {\n  if (typeof value !== \"string\" || !value || value.length > 2048) return false;\n  try {\n    const u = new URL(value);\n    return u.protocol === \"https:\" && !u.username && !u.password &&\n      u.pathname === \"/\" && !u.search && !u.hash && u.origin === value;\n  } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Store origins with no trailing slash, path, query, credentials, or port ambiguity","Only write identity rows through applyCloudRuntimeIdentityAssertion, never hand-edit instance_settings","Validate origins with the same exactHttpsOrigin rules in any custom migration tooling","Use https:// URLs from the Cloud control plane verbatim"],"tags":["cloud","identity","url-validation","startup"],"backgroundTag":"invalid-url-format","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"}