{"record":{"id":"03997d25cabb2613","repo":"paperclipai/paperclip","slug":"native-runner-authority-rotation-state-unsafe","errorCode":"native_runner_authority_rotation_state_unsafe","errorMessage":"native_runner_authority_rotation_state_unsafe","messagePattern":"native_runner_authority_rotation_state_unsafe","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/runnerd-codex-transport.ts","lineNumber":170,"sourceCode":"  const metadata = lstatSync(path);\n  if (\n    metadata.isSymbolicLink() ||\n    !metadata.isFile() ||\n    metadata.size > 64 * 1024 * 1024\n  ) {\n    throw new Error(\"native_runner_control_plane_state_unsafe\");\n  }\n  return record(JSON.parse(readFileSync(path, \"utf8\")));\n}\n\nfunction readRunnerState(path: string): Record<string, unknown> {\n  const metadata = lstatSync(path);\n  if (\n    metadata.isSymbolicLink() ||\n    !metadata.isFile() ||\n    metadata.size > 16 * 1024 * 1024\n  ) {\n    throw new Error(\"native_runner_authority_rotation_state_unsafe\");\n  }\n  return record(JSON.parse(readFileSync(path, \"utf8\")));\n}\n\nfunction controlPlaneIdentity(\n  state: Record<string, unknown>,\n): DurableRecoveryIdentity {\n  return structuredClone(\n    record(state.identity) as unknown as DurableRecoveryIdentity,\n  );\n}\n\nfunction recoveryIdentityMatches(\n  value: DurableRecoveryIdentity | Record<string, unknown>,\n  expected: DurableRecoveryIdentity,\n): boolean {\n  return (\n    value.runnerInstanceId === expected.runnerInstanceId &&","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/runnerd-codex-transport.ts#L152-L188","documentation":"readRunnerState() applies the same safety validation as the control-plane reader but to runner-state.json, with a tighter 16 MiB cap. It throws native_runner_authority_rotation_state_unsafe when the file is a symlink, not a regular file, or oversized, because the runner state file backs authority epoch rotation and must not come from an untrusted or anomalous path.","triggerScenarios":"Calling runnerState() or candidateRunnerState() (or triggering rotateExternalAuthorityEpoch/#closeOnce flows) while runner-state.json is a symlink, special file, or larger than 16 MiB.","commonSituations":"User symlinked runner-state.json to a shared config; a previous crash left a corrupted multi-megabyte state file; tests running against tmpdirs with odd file types; antivirus or sync tools (Dropbox) replacing the file with links.","solutions":["Check `ls -la runner-state.json`; remove symlinks and restore a plain regular file","Verify size is under 16 MiB; truncate/regenerate the state file from a known-good copy","Exclude the runner state directory from symlink-creating sync/backup tools","If unrecoverable, archive the whole state root and let the runner re-initialize it"],"exampleFix":"// before\nconst st = transport.runnerState();\n// after\nconst st = fs.lstatSync(runnerStatePath);\nif (!st.isSymbolicLink() && fs.statSync(runnerStatePath).isFile() && st.size <= 16 * 1024 * 1024) {\n  const state = transport.runnerState();\n}","handlingStrategy":"validation","validationCode":"import { lstatSync, statSync } from \"node:fs\";\nfunction isSafeRunnerState(p: string): boolean {\n  const m = lstatSync(p);\n  return !m.isSymbolicLink() && statSync(p).isFile() && m.size <= 16 * 1024 * 1024;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const state = transport.runnerState();\n} catch (err) {\n  if (err.code === \"native_runner_authority_rotation_state_unsafe\") {\n    logger.error(\"runner-state.json unsafe (symlink/special/oversized)\");\n    restoreRunnerStateFromBackup();\n  } else throw err;\n}","preventionTips":["Keep runner-state.json a plain regular file in a real directory","Keep the state dir out of dotfile managers and sync tools","Alert on abnormal growth of runner-state.json (16 MiB cap)","Validate state after crashes and restore from archive backups rather than hand-editing"],"tags":["filesystem","security","state-file","codex"],"backgroundTag":"invalid-argument-value","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"}