Yeachan-Heo/oh-my-codex · error · Error

autoresearch_reset_requires_clean_worktree

autoresearch_reset_requires_clean_worktree

Error message

autoresearch_reset_requires_clean_worktree:${worktreePath}:${blocking.join(' | ')}

What it means

assertResetSafeWorktree throws (code autoresearch_reset_requires_clean_worktree) when `git status --porcelain` in the worktree shows dirty entries that are not on the allowlist (isAllowedRuntimeDirtyLine). The runtime refuses to reset a worktree that contains uncommitted user changes, because a reset would destroy them.

Source

Thrown at src/autoresearch/runtime.ts:262

    .split(/\r?\n/)
    .map((line) => line.trimEnd())
    .filter(Boolean);
}

function isAllowedRuntimeDirtyLine(line: string): boolean {
  const trimmed = line.trim();
  if (trimmed.length < 4) return false;
  const path = trimmed.slice(3).trim();
  return trimmed.startsWith('?? ') && AUTORESEARCH_WORKTREE_EXCLUDES.some((exclude) => exclude.endsWith('/')
    ? path.startsWith(exclude) || path === exclude.slice(0, -1)
    : path === exclude);
}

export function assertResetSafeWorktree(worktreePath: string): void {
  const lines = gitStatusLines(worktreePath);
  const blocking = lines.filter((line) => !isAllowedRuntimeDirtyLine(line));
  if (blocking.length === 0) return;
  throw new Error(`autoresearch_reset_requires_clean_worktree:${worktreePath}:${blocking.join(' | ')}`);
}

async function ensureParentDir(filePath: string): Promise<void> {
  await mkdir(dirname(filePath), { recursive: true });
}

async function writeJsonFile(filePath: string, value: unknown): Promise<void> {
  await ensureParentDir(filePath);
  await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
}

async function readJsonFile<T>(filePath: string): Promise<T> {
  return JSON.parse(await readFile(filePath, 'utf-8')) as T;
}

async function readActiveRunState(projectRoot: string): Promise<AutoresearchActiveRunState | null> {
  const file = activeRunStateFile(projectRoot);
  if (!existsSync(file)) return null;

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Inspect the blocking entries listed in the message and commit, stash, or remove them inside the worktree
  2. If the changes are disposable, `git checkout -- . && git clean -fd` in the worktree path
  3. If they are valuable, commit them to a branch or copy them out before resuming/resetting
  4. Report/extend isAllowedRuntimeDirtyLine if a legitimate runtime artifact is wrongly flagged

Example fix

# before
# worktree has modified src/foo.ts
resumeAutoresearchRuntime(repoRoot, runId); // throws autoresearch_reset_requires_clean_worktree
# after
git -C /path/to/worktree stash --include-untracked
resumeAutoresearchRuntime(repoRoot, runId);
Defensive patterns

Strategy: validation

Validate before calling

import { spawnSync } from 'node:child_process';
function isClean(worktreePath: string): boolean {
  const r = spawnSync('git', ['status', '--porcelain', '--untracked-files=all'], { cwd: worktreePath, encoding: 'utf-8' });
  return r.status === 0 && (r.stdout ?? '').trim() === '';
}

Try / catch

try { await resumeAutoresearchRuntime(repoRoot, runId); }
catch (e) { if (e instanceof Error && e.message.startsWith('autoresearch_reset_requires_clean_worktree')) { const dirty = e.message.split(':').pop(); /* stash/commit those paths, then retry */ } throw e; }

Prevention

When it happens

Trigger: Calling prepareAutoresearchRuntime, resumeAutoresearchRuntime, resetToLastKeptCommit, or recordNonEvaluatedCandidateStatus while the worktree has modified/untracked files beyond the runtime's own allowed artifacts (e.g. .omx logs or exclusions).

Common situations: A developer manually edited files inside the autoresearch worktree, a crashed run left partial changes, editors creating temp/untracked files, or a previous run's artifacts not covered by the allowlist.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/131c98b34f0c4918. Report an issue: GitHub.