{"record":{"id":"e3f4349c025c72e3","repo":"Yeachan-Heo/oh-my-codex","slug":"unsafe-autopilot-context-snapshot-path-existing","errorCode":null,"errorMessage":"Unsafe Autopilot context snapshot path: ${existingSnapshot.path}","messagePattern":"Unsafe Autopilot context snapshot path: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/hooks/keyword-detector.ts","lineNumber":421,"sourceCode":"  throw new Error(`Unable to allocate unique Autopilot context snapshot for ${slug}`);\n}\n\nasync function ensureAutopilotContextSnapshot(\n  sourceCwd: string,\n  nowIso: string,\n  activationText: string,\n  existingSnapshot?: AutopilotContextSnapshotDescriptor,\n  options: { allowTaskSnapshotCreation?: boolean; recoveryReason?: AutopilotContextRecoveryReason } = {},\n): Promise<AutopilotContextSnapshotResult> {\n  if (existingSnapshot) {\n    if (isSafeAutopilotContextSnapshotPath(existingSnapshot.path)) {\n      return {\n        path: existingSnapshot.path,\n        kind: existingSnapshot.kind,\n        original_task_status: existingSnapshot.kind === 'legacy' ? 'legacy-unverified' : 'activation-prompt',\n      };\n    }\n    throw new Error(`Unsafe Autopilot context snapshot path: ${existingSnapshot.path}`);\n  }\n\n  if (options.allowTaskSnapshotCreation === false) {\n    const slug = 'autopilot-recovery';\n    const continuationInput = activationText.trim() || '<empty>';\n    const reason = options.recoveryReason ?? 'missing-or-unsafe-legacy-context-snapshot';\n    const body = [\n      '# Autopilot context recovery',\n      '',\n      '- recovery status: degraded',\n      `- recovery reason: ${reason}`,\n      `- reason detail: ${AUTOPILOT_CONTEXT_RECOVERY_REASON_MESSAGES[reason]}`,\n      `- continuation input: ${continuationInput}`,\n      '- original task status: unavailable',\n      '- original task seed: unavailable; do not treat the continuation input as the task seed.',\n      '- required follow-up: re-establish or confirm the intended task context before downstream handoff.',\n      '',\n    ].join('\\n');","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/hooks/keyword-detector.ts#L403-L439","documentation":"Thrown by ensureAutopilotContextSnapshot when an existing snapshot record is found but its path fails the isSafeAutopilotContextSnapshotPath check (e.g. absolute path, traversal, or outside the expected context directory). The hook validates legacy/prior snapshot paths before reusing them and refuses unsafe ones.","triggerScenarios":"A prior run recorded a snapshot whose path is absolute, contains '..', points outside .omx/context, or was written by an older layout the safety check doesn't recognize; the detector then refuses to reuse it and throws before falling back to creation.","commonSituations":"Upgrading the hook to a version with stricter path validation while old snapshot metadata contains absolute or legacy-relative paths; manually edited/corrupted snapshot index files; snapshots recorded when the repo lived at a different path; partial migrations leaving mismatched path formats.","solutions":["Delete or reset the stale snapshot record (and its file) under .omx/context so a fresh, safely-pathed snapshot is created","Regenerate the snapshot index from scratch if it's a metadata file (back it up first)","If you need the old snapshot, copy its contents into a new run rather than reusing the unsafe path","Check the isSafeAutopilotContextSnapshotPath requirements and ensure your storage layer writes relative paths under .omx/context"],"exampleFix":"# before\n# existing snapshot metadata has path '/abs/old/repo/.omx/context/snap.json'\nrun-hook  # throws\n\n# after\nrm -rf .omx/context  # or remove just the offending snapshot record\nrun-hook  # creates a fresh snapshot with a safe relative path","handlingStrategy":"try-catch","validationCode":"import { isAbsolute, relative } from 'node:path';\n\nfunction snapshotPathIsSafe(contextDir: string, p: unknown): p is string {\n  if (typeof p !== 'string' || !p.trim() || isAbsolute(p)) return false;\n  const rel = relative(contextDir, join(contextDir, p));\n  return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);\n}","typeGuard":"function isSafeSnapshotRecord(r: { path: unknown }): boolean {\n  return typeof r.path === 'string' && !r.path.startsWith('/') && !r.path.includes('..');\n}","tryCatchPattern":"try { snap = await ensureAutopilotContextSnapshot(cwd, iso, text); }\ncatch (err) {\n  if ((err as Error).message.includes('Unsafe Autopilot context snapshot path')) {\n    await resetContextSnapshots(cwd); // remove stale snapshot index/records\n    snap = await ensureAutopilotContextSnapshot(cwd, iso, text);\n  } else throw err;\n}","preventionTips":["Store snapshot paths as relative paths under .omx/context from day one","Reset snapshot metadata after major version upgrades of the hook","Validate persisted paths against the context dir on load, not just on reuse"],"tags":["path-safety","snapshot","legacy-data","validation"],"backgroundTag":"unsafe-stored-path-rejected","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}