ruvnet/ruflo · error · Error

repository changed while capturing exact source state

Error message

repository changed while capturing exact source state

What it means

On the clean-worktree fast path, the collector reads `git status --porcelain=v1 -z --untracked-files=all --ignore-submodules=none` and HEAD twice, around its tree-id reads. If the second status is non-empty or HEAD moved, the repository mutated during capture and the error aborts rather than binding a sourceStateId to a state that no longer exists.

Source

Thrown at v3/@claude-flow/codex/src/harness/repository-state.ts:426

  const requested = repoPath instanceof URL ? fileURLToPath(repoPath) : repoPath;
  const repoRoot = realpathSync(resolve(requested));
  const top = realpathSync(gitText(repoRoot, ['rev-parse', '--show-toplevel']));
  if (top !== repoRoot) throw new Error(`repoPath must be the Git top-level: ${top}`);

  const cleanStatus = (): Buffer => gitBuffer(repoRoot, [
    'status',
    '--porcelain=v1',
    '-z',
    '--untracked-files=all',
    '--ignore-submodules=none',
  ]);
  const initialStatus = cleanStatus();
  if (initialStatus.length === 0) {
    repositoryPaths(repoRoot, false);
    const baseCommit = gitText(repoRoot, ['rev-parse', '--verify', 'HEAD']);
    const treeId = gitText(repoRoot, ['rev-parse', '--verify', 'HEAD^{tree}']);
    if (cleanStatus().length !== 0 || gitText(repoRoot, ['rev-parse', '--verify', 'HEAD']) !== baseCommit) {
      throw new Error('repository changed while capturing exact source state');
    }
    const repository = repositoryIdentity(repoRoot);
    const identity = {
      version: 1,
      scope: 'git-visible',
      repositoryId: repository.repositoryId,
      commit: baseCommit,
      tree: treeId,
    };
    return {
      version: 1,
      scope: 'git-visible',
      kind: 'clean',
      clean: true,
      repository,
      baseCommit,
      treeId,
      sourceStateId: digest(canonicalJson(identity)),

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Ensure no other git writers run against the worktree during capture and retry
  2. Disable IDE auto-fetch and background git hooks for the capture window
  3. Prefer capturing from a clean checkout dedicated to the capture
Defensive patterns

Strategy: retry

Validate before calling

const headA = gitText(root, ['rev-parse', 'HEAD']);
const statusA = gitText(root, ['status', '--porcelain=v1', '-z']);
const headB = gitText(root, ['rev-parse', 'HEAD']);
const still = headA === headB && statusA === gitText(root, ['status', '--porcelain=v1', '-z']);

Try / catch

try { return capture(root); }
catch (e) {
  if (/repository changed while capturing/.test(String(e)) && quiesced) return capture(root);
  throw e;
}

Prevention

When it happens

Trigger: Anything touching the repo between the two reads: a concurrent `git fetch` updating refs, a hook or editor committing, an auto-formatter writing files, another agent session in the same worktree.

Common situations: Snapshotting while a CI checkout step, IDE auto-fetch, or another agent runs in the same clone; release tooling capturing evidence while a background job commits.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/a1dec23ef58900c0. Report an issue: GitHub.