ruvnet/ruflo · error · Error

repository changed while hashing untracked entry: ${path}

Error message

repository changed while hashing untracked entry: ${path}

What it means

untrackedManifest() lstat's each untracked entry before and after reading its content and compares mode, size, mtimeMs, and ino. Any difference means the file changed between the two stats (a TOCTOU window), so the capture refuses to record a torn state instead of hashing a half-written file.

Source

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

    let kind: UntrackedFileIdentity['kind'];
    let content: Buffer;
    if (before.isSymbolicLink()) {
      kind = 'symlink';
      content = Buffer.from(readlinkSync(absolute), 'utf8');
    } else if (before.isFile()) {
      kind = 'file';
      content = readFileSync(absolute);
    } else {
      throw new Error(`unsupported untracked repository entry: ${path}`);
    }
    const after = lstatSync(absolute);
    if (
      before.mode !== after.mode
      || before.size !== after.size
      || before.mtimeMs !== after.mtimeMs
      || before.ino !== after.ino
    ) {
      throw new Error(`repository changed while hashing untracked entry: ${path}`);
    }
    return {
      path,
      kind,
      mode: before.mode & 0o7777,
      bytes: content.byteLength,
      digest: digest(content),
    };
  });
  return { digest: digest(canonicalJson(entries)), entries };
}

function trackedPatch(repoRoot: string): ContentDigest {
  return contentDigest(gitBuffer(repoRoot, [
    'diff',
    '--binary',
    '--full-index',
    '--no-ext-diff',

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Stop the concurrent writer (watchers, builds, other agent sessions) and re-run the capture
  2. Commit or stash work so the tree is quiescent before capturing
  3. Retry the capture — transient writes usually clear on the second attempt
Defensive patterns

Strategy: retry

Validate before calling

function isQuiescent(abs: string): boolean {
  const a = lstatSync(abs);
  const content = readFileSync(abs);
  const b = lstatSync(abs);
  return a.mtimeMs === b.mtimeMs && a.size === b.size && a.ino === b.ino;
}

Try / catch

for (let attempt = 1; attempt <= 3; attempt++) {
  try { return capture(); }
  catch (e) { if (!/changed while hashing/.test(String(e))) throw e; }
}

Prevention

When it happens

Trigger: Any process writes to an untracked file during capture: build output, `npm install`, formatters, watch mode, another agent or editor saving the file mid-read.

Common situations: Running capture while `npm run build`, a bundler --watch, or another AI agent is actively writing to the worktree; CI pipelines that generate artifacts during the same phase as the snapshot.

Related errors


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