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.

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.

Appendix: 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)