ruvnet/ruflo · error
concurrent writers must use distinct worktrees: ${writerPath
Error message
concurrent writers must use distinct worktrees: ${writerPath} What it means
After requiring isolated worktrees, the scheduler also verifies that no two workers in the same execution level resolve to the same directory: it collects path.resolve(worker.worktreePath ?? config.projectPath) for every non-readOnly worker in the level and throws when a path repeats. This catches two writers assigned the same worktree, or distinct worktreePath strings that resolve to one location (symlinks, duplicated segments).
Source
Thrown at v3/@claude-flow/codex/src/dual-mode/orchestrator.ts:443
for (const worker of level) {
placed.add(worker.id);
}
if (level.length > 0) {
levels.push(level);
}
}
for (const level of levels) {
const writerPaths = new Set<string>();
const writers = level.filter((item) => !item.readOnly);
for (const worker of writers) {
if (this.config.worktreeIsolation && !worker.worktreePath) {
throw new Error(`writer ${worker.id} requires an isolated worktree`);
}
const writerPath = path.resolve(worker.worktreePath ?? this.config.projectPath);
if (this.config.worktreeIsolation && writerPaths.has(writerPath)) {
throw new Error(`concurrent writers must use distinct worktrees: ${writerPath}`);
}
writerPaths.add(writerPath);
}
}
return levels;
}
/** Preserve read-only parallelism while independently bounding writers. */
private partitionLevel(level: WorkerConfig[]): WorkerConfig[][] {
const remaining = [...level];
const batches: WorkerConfig[][] = [];
while (remaining.length > 0) {
const batch: WorkerConfig[] = [];
let writers = 0;
for (let index = 0; index < remaining.length && batch.length < this.config.maxConcurrent;) {
const candidate = remaining[index]!;
if (!candidate.readOnly && writers >= this.config.maxWriters) {
index += 1;View on GitHub (pinned to fa13ee4ad6)
Solutions
- Give each writer its own unique worktreePath — parameterize the path by worker id when generating worktrees
- If both writers must touch the same checkout, serialize them: add a dependsOn edge so they land in different levels and never run concurrently
- If one of the two only reads, mark it readOnly: true so it is excluded from the writer-path set
Example fix
// before
const workers = [
{ id: 'coder-1', role: 'coder', worktreePath: '/repo/worktrees/shared' },
{ id: 'coder-2', role: 'coder', worktreePath: '/repo/worktrees/shared' }, // same level, same path
];
// after
const workers = ['coder-1', 'coder-2'].map((id) => ({
id,
role: 'coder',
worktreePath: `/repo/worktrees/${id}`,
})); Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path';
function assertDistinctWriterWorktrees(config: OrchestratorConfig, workers: WorkerConfig[]): void {
const seen = new Set<string>();
for (const w of workers.filter((x) => !x.readOnly)) {
const p = path.resolve(w.worktreePath ?? config.projectPath);
if (seen.has(p)) throw new Error(`two writers share worktree ${p}`);
seen.add(p);
}
} Try / catch
Catch and treat as configuration: extract the duplicated path from the message, then either give each writer its own worktree or add a dependsOn edge to serialize them before re-running.
Prevention
- Always derive worktreePath from the worker id (parameterized template), never copy-paste paths
- Review the writer set of each dependency level when adding workers to an existing swarm
- Keep the one-writer-per-worktree rule in code review checklists for orchestrator configs
When it happens
Trigger: Two non-readOnly workers with no dependsOn edge between them (same level) share an identical worktreePath, or their worktreePath values are different strings that resolve to the same directory, such as via symlinks or redundant ../ segments.
Common situations: Generating worktrees in a loop but assigning every worker index 0's path; pointing several writers at one shared checkout while worktreeIsolation is on; re-using a worktree template string without parameterizing it per worker.
Related errors
- writer ${worker.id} requires an isolated worktree
- maxWriters must be a positive integer
- Invalid completion type
- MCP server "${server.name}" returned HTTP ${httpStatus}: ${h
- No endpoints configured. This build requires OpenAI-compatib
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/cc94cdcbb70560a6.
Report an issue: GitHub.