Yeachan-Heo/oh-my-codex · error · Error
Unsafe Autopilot context directory: .omx/context is a symbol
Error message
Unsafe Autopilot context directory: .omx/context is a symbolic link
What it means
Thrown by ensureSafeAutopilotContextDir when .omx/context is a symbolic link. Same rationale as the .omx check: snapshot writes must stay inside the real repository tree, so a symlinked context directory is rejected via lstat before any write happens.
Source
Thrown at src/hooks/keyword-detector.ts:371
const AUTOPILOT_CONTEXT_RECOVERY_REASON_MESSAGES: Record<AutopilotContextRecoveryReason, string> = {
'missing-or-unsafe-legacy-context-snapshot': 'no safe legacy Autopilot context snapshot path was available during continuation.',
'missing-autopilot-mode-state': 'active Autopilot skill state existed but no matching Autopilot mode state was available during continuation.',
'malformed-autopilot-mode-state': 'active Autopilot mode state could not be parsed during continuation.',
'nonpreservable-autopilot-mode-state-missing-current-phase': 'active Autopilot mode state was missing current_phase during continuation.',
};
async function ensureSafeAutopilotContextDir(sourceCwd: string): Promise<string> {
const rootRealPath = await realpath(sourceCwd);
const omxDir = join(sourceCwd, '.omx');
await mkdir(omxDir, { recursive: true });
if ((await lstat(omxDir)).isSymbolicLink()) {
throw new Error('Unsafe Autopilot context directory: .omx is a symbolic link');
}
const contextDir = join(omxDir, 'context');
await mkdir(contextDir, { recursive: true });
if ((await lstat(contextDir)).isSymbolicLink()) {
throw new Error('Unsafe Autopilot context directory: .omx/context is a symbolic link');
}
const contextRealPath = await realpath(contextDir);
const relativeToRoot = relative(rootRealPath, contextRealPath);
if (relativeToRoot === '' || relativeToRoot.startsWith('..') || isAbsolute(relativeToRoot)) {
throw new Error('Unsafe Autopilot context directory: resolved path escapes repository root');
}
return contextDir;
}
async function writeUniqueAutopilotContextSnapshot(
sourceCwd: string,
slug: string,
nowIso: string,
body: string,
): Promise<string> {
const contextDir = await ensureSafeAutopilotContextDir(sourceCwd);
const timestamp = utcCompactTimestamp(nowIso);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Remove the symlink and recreate a real directory: rm .omx/context && mkdir .omx/context
- Migrate old snapshots by copying (cp -r) rather than linking, then delete the link
- Check for tooling in your workflow that creates symlinks under .omx and disable it for this path
Example fix
# before .omx/context -> /tmp/autopilot-context (symlink) # after rm .omx/context mkdir .omx/context
Defensive patterns
Strategy: validation
Validate before calling
import { lstat } from 'node:fs/promises';
async function contextIsSafe(cwd: string): Promise<boolean> {
try { return !(await lstat(join(cwd, '.omx', 'context'))).isSymbolicLink(); }
catch { return true; }
} Try / catch
try { await ensureAutopilotContextSnapshot(cwd, iso, text); } catch (err) {
if ((err as Error).message.includes('.omx/context is a symbolic link')) {
await rm(join(cwd, '.omx', 'context'), { force: true });
await mkdir(join(cwd, '.omx', 'context'), { recursive: true });
return ensureAutopilotContextSnapshot(cwd, iso, text);
}
throw err;
} Prevention
- Keep .omx/context as a plain directory; move old snapshots with cp, not ln -s
- Audit cleanup/dedup tooling so it never replaces context with a link
- Include a symlink check in repo preflight scripts
When it happens
Trigger: Running the Autopilot hook when .omx exists as a real directory but .omx/context is a symlink (e.g. points to a temp dir or another project's context).
Common situations: Manually moving context snapshots elsewhere and linking back; cleanup scripts that replaced the dir with a link; tools that 'deduplicate' directories by symlinking; partially-migrated repos where only context was linked.
Related errors
- Unsafe Autopilot context directory: .omx is a symbolic link
- Refusing cancellation through non-regular run state target:
- Refusing cancellation through non-regular state target ${ref
- Refusing to use unsafe backup ancestor ${currentPath}.
- run directory escapes the authorized runs root
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/ed6a2001c3385e77.
Report an issue: GitHub.