{"record":{"id":"3af1c1a634da060d","repo":"paperclipai/paperclip","slug":"native-runner-control-plane-state-unsafe","errorCode":"native_runner_control_plane_state_unsafe","errorMessage":"native_runner_control_plane_state_unsafe","messagePattern":"native_runner_control_plane_state_unsafe","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/runnerd-codex-transport.ts","lineNumber":158,"sourceCode":"\nexport function withCodexCollaborationRuntimeInstructions(\n  instructions: string,\n  enabled = true,\n): string {\n  if (!enabled) return instructions;\n  const base = instructions.trimEnd();\n  return `${base}\\n\\n${CODEX_COLLABORATION_RUNTIME_INSTRUCTIONS}`;\n}\n\nfunction readControlPlaneState(directory: string): Record<string, unknown> {\n  const path = resolve(directory, \"control-plane-state.json\");\n  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>,","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/runnerd-codex-transport.ts#L140-L176","documentation":"readControlPlaneState() validates the on-disk control-plane state file with lstatSync before reading it. It refuses to load anything that is a symlink, not a regular file, or larger than 64 MiB, throwing native_runner_control_plane_state_unsafe. This is a tamper/safety guard: the control-plane state drives authority and resume decisions, so an unexpected file shape must not be trusted or parsed.","triggerScenarios":"The control-plane state path is a symlink, a socket/directory/device instead of a regular file, or has grown beyond the 64 MiB cap when state(), archivedIdentity(), or #resume reads it.","commonSituations":"Operator replaced the state file with a symlink into a mount; a runaway writer corrupted/blew up the state file; running the runner against a bind-mounted or container-virtualized path where lstat attributes differ.","solutions":["Inspect the file: `ls -la` the path; replace any symlink with a real regular file","Check file size (`stat -c %s`); if over 64 MiB, restore state from a valid backup or re-initialize the runner state directory","If the state directory is corrupted, let the quarantine/re-init path recreate it rather than hand-editing the file","Verify the runner is pointed at the correct state root (config mistake can make it read a foreign file)"],"exampleFix":"// before\nconst state = transport.state; // throws native_runner_control_plane_state_unsafe\n// after\nimport { statSync } from \"node:fs\";\nconst meta = statSync(controlPlaneStatePath);\nif (meta.isFile() && meta.size <= 64 * 1024 * 1024 && !lstatSync(controlPlaneStatePath).isSymbolicLink()) {\n  const state = transport.state;\n}","handlingStrategy":"validation","validationCode":"import { lstatSync, statSync } from \"node:fs\";\nfunction isSafeStateFile(p: string): boolean {\n  const m = lstatSync(p);\n  return !m.isSymbolicLink() && statSync(p).isFile() && m.size <= 64 * 1024 * 1024;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const state = transport.state;\n} catch (err) {\n  if (err.code === \"native_runner_control_plane_state_unsafe\") {\n    logger.error(\"control-plane state file unsafe; restore from backup\", { path });\n    await recoverFromQuarantineOrReinit();\n  } else throw err;\n}","preventionTips":["Never symlink runner state files; configure the state root as a real directory","Exclude state directories from cloud-sync/backup tools that create links or grow files","Monitor state file size; alert before the 64 MiB cap","Run the runner with correct state-root config to avoid reading foreign files"],"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"}