Yeachan-Heo/oh-my-codex · error · Error
Unable to allocate unique Autopilot context snapshot for ${s
Error message
Unable to allocate unique Autopilot context snapshot for ${slug} What it means
Thrown by writeUniqueAutopilotContextSnapshot when it exhausts its allocation attempts: it tries to create snapshot files with an exclusive (O_EXCL-style) create, and on every attempt the candidate filename already exists (EEXIST), eventually exceeding the retry budget for the given slug. Non-EEXIST errors propagate immediately.
Source
Thrown at src/hooks/keyword-detector.ts:403
nowIso: string,
body: string,
): Promise<string> {
const contextDir = await ensureSafeAutopilotContextDir(sourceCwd);
const timestamp = utcCompactTimestamp(nowIso);
for (let attempt = 0; attempt < 100; attempt += 1) {
const suffix = attempt === 0 ? '' : `-${attempt + 1}`;
const filename = `${slug}-${timestamp}${suffix}.md`;
const relativePath = `.omx/context/${filename}`;
const absolutePath = resolve(contextDir, filename);
try {
await writeFile(absolutePath, body, { encoding: 'utf-8', flag: 'wx' });
return relativePath;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'EEXIST') continue;
throw error;
}
}
throw new Error(`Unable to allocate unique Autopilot context snapshot for ${slug}`);
}
async function ensureAutopilotContextSnapshot(
sourceCwd: string,
nowIso: string,
activationText: string,
existingSnapshot?: AutopilotContextSnapshotDescriptor,
options: { allowTaskSnapshotCreation?: boolean; recoveryReason?: AutopilotContextRecoveryReason } = {},
): Promise<AutopilotContextSnapshotResult> {
if (existingSnapshot) {
if (isSafeAutopilotContextSnapshotPath(existingSnapshot.path)) {
return {
path: existingSnapshot.path,
kind: existingSnapshot.kind,
original_task_status: existingSnapshot.kind === 'legacy' ? 'legacy-unverified' : 'activation-prompt',
};
}
throw new Error(`Unsafe Autopilot context snapshot path: ${existingSnapshot.path}`);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Reduce parallelism or stagger invocations so timestamps differ (pass distinct nowIso values per invocation)
- Clean up old snapshot files for the slug under .omx/context if they're stale/disposable
- Pass a more unique slug per invocation so candidate names don't collide
- Report upstream if legitimate sequential usage exhausts the attempt budget — the retry count may need increasing
Example fix
// before
for (const item of items) await ensureAutopilotContextSnapshot(cwd, sameIso, item.slug); // same slug+time repeatedly
// after
for (const item of items) {
await ensureAutopilotContextSnapshot(cwd, new Date().toISOString(), `${item.slug}-${crypto.randomUUID().slice(0,6)}`);
} Defensive patterns
Strategy: retry
Validate before calling
import { access } from 'node:fs/promises';
async function snapshotNameLikelyFree(dir: string, slug: string, iso: string): Promise<boolean> {
const base = `${slug}-${iso.replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z')}`;
try { await access(join(dir, base + '.json')); return false; } catch { return true; }
} Try / catch
for (let i = 0; i < 3; i++) {
try { return await ensureAutopilotContextSnapshot(cwd, new Date().toISOString(), slug); }
catch (err) {
if (!(err as Error).message.includes('Unable to allocate unique Autopilot context snapshot')) throw err;
slug = `${slug}-${crypto.randomUUID().slice(0, 6)}`;
}
}
throw new Error('snapshot allocation failed'); Prevention
- Pass distinct nowIso values when invoking in loops or parallel workers
- Make slugs unique per invocation (append a short random suffix)
- Periodically prune stale snapshots to keep the namespace from filling
When it happens
Trigger: Many snapshots created within the same timestamp window for the same slug (e.g. a loop or fan-out invoking the hook dozens of times in the same second), so all candidate suffixed filenames are taken; or a hostile/pre-existing set of files squatting every candidate name for that slug.
Common situations: Parallel test suites triggering the hook hundreds of times; automation retry storms re-invoking the detector with the same slug and timestamp; clock skew making 'now' identical across many invocations; leftover snapshot files filling the namespace.
Related errors
- autoresearch_candidate_missing:${candidateFile}
- plugin_skill_mirror_out_of_sync\nkind=${mismatch.kind}\n${mi
- Refusing cancellation through non-regular run state target:
- Refusing cancellation through non-regular state target ${ref
- Refusing cancellation outside authorized state root: ${ref.p
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/ffa2aa9cc17a9961.
Report an issue: GitHub.