ruvnet/ruflo · error · Error

refusing to prepare writing worktrees from a dirty repositor

Error message

refusing to prepare writing worktrees from a dirty repository

What it means

prepare() refuses to create writing worktrees when `git status --porcelain` in repoRoot reports anything and options.allowDirty is not true. Writing worktrees branch from committed HEAD, so uncommitted state would silently diverge between the main checkout and agent worktrees; the coordinator fails fast instead of proceeding from an inexact source state.

Source

Thrown at v3/@claude-flow/codex/src/worktrees/coordinator.ts:63

  }

  prepare(
    runId: string,
    agents: Array<{ id: string; readOnly?: boolean }>,
    options: { allowDirty?: boolean; baseRef?: string } = {},
  ): WorktreeRunRecord {
    assertId(runId, 'run id');
    if (!agents.length) throw new Error('at least one agent is required');
    const unique = new Set<string>();
    for (const agent of agents) {
      assertId(agent.id, 'agent id');
      if (unique.has(agent.id)) throw new Error(`duplicate agent id: ${agent.id}`);
      unique.add(agent.id);
    }
    const registryPath = this.registryPath(runId);
    if (existsSync(registryPath)) return this.status(runId);
    if (!options.allowDirty && git(this.repoRoot, ['status', '--porcelain']).length > 0) {
      throw new Error('refusing to prepare writing worktrees from a dirty repository');
    }

    mkdirSync(this.registryDir, { recursive: true });
    mkdirSync(join(this.worktreeBase, runId), { recursive: true });
    const assignments: WorktreeAssignment[] = [];
    try {
      for (const agent of agents) {
        const readOnly = agent.readOnly === true;
        const branch = `ruflo/${runId}/${agent.id}`;
        const worktreePath = join(this.worktreeBase, runId, agent.id);
        if (readOnly) {
          git(this.repoRoot, ['worktree', 'add', '--detach', worktreePath, options.baseRef ?? 'HEAD']);
        } else {
          git(this.repoRoot, ['worktree', 'add', '-b', branch, worktreePath, options.baseRef ?? 'HEAD']);
        }
        assignments.push({ agentId: agent.id, branch: readOnly ? '' : branch, path: worktreePath, readOnly });
      }
      const record: WorktreeRunRecord = {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Commit or stash the changes (git stash, or git add + git commit) so `git status --porcelain` is empty, then re-run prepare()
  2. Add .gitignore entries for generated artifacts and clean untracked files (preview with `git clean -nd`)
  3. If a dirty base is acceptable, call prepare(runId, agents, { allowDirty: true }) — agent worktrees will then branch from HEAD without the uncommitted changes

Example fix

// before
coordinator.prepare('run-1', agents); // throws: dirty repository

// after
execSync('git stash --include-untracked', { cwd: repoRoot });
coordinator.prepare('run-1', agents);
Defensive patterns

Strategy: validation

Validate before calling

import { execFileSync } from 'node:child_process';
function isClean(repoRoot: string): boolean {
  return execFileSync('git', ['-C', repoRoot, 'status', '--porcelain'], { encoding: 'utf8' }).trim().length === 0;
}
if (!isClean(repoRoot) && !allowDirty) {
  throw new Error('repo is dirty — commit, stash, or pass allowDirty');
}
coordinator.prepare(runId, agents, { allowDirty });

Try / catch

try {
  coordinator.prepare(runId, agents);
} catch (e) {
  if (e instanceof Error && e.message.includes('dirty repository')) {
    // stash and retry once, or surface to the operator
  } else throw e;
}

Prevention

When it happens

Trigger: prepare(runId, agents) with default options while the repository has staged, modified, or untracked files. A single untracked file that is not gitignored is enough to trigger it.

Common situations: Local development with leftover edits; a previous agent wrote into the main checkout instead of its worktree; CI jobs that generate files (dist/, coverage/, logs) without gitignore entries.

Related errors


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