ruvnet/ruflo · error

worker dependency cycle: ${remaining.join(', ')}

Error message

worker dependency cycle: ${remaining.join(', ')}

What it means

Thrown by the layered scheduler in DualModeOrchestrator when it topologically sorts WorkerConfig entries by dependsOn. Each pass places workers whose dependencies are already placed; if a pass places zero workers while some remain unplaced, the dependency graph can never be satisfied and the error lists the stuck worker ids. Besides true cycles (A depends on B while B depends on A, or a self-dependency), it also fires when dependsOn names an id that is not present in the workers array, because that dependency is never placed.

Source

Thrown at v3/@claude-flow/codex/src/dual-mode/orchestrator.ts:422

    const placed = new Set<string>();

    while (placed.size < workers.length) {
      const level: WorkerConfig[] = [];

      for (const worker of workers) {
        if (placed.has(worker.id)) continue;

        const depsReady = !worker.dependsOn ||
          worker.dependsOn.every(dep => placed.has(dep));

        if (depsReady) {
          level.push(worker);
        }
      }

      if (level.length === 0 && placed.size < workers.length) {
        const remaining = workers.filter((worker) => !placed.has(worker.id)).map((worker) => worker.id);
        throw new Error(`worker dependency cycle: ${remaining.join(', ')}`);
      }

      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`);
        }

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Read the worker ids in the error message — they are exactly the workers that could never be scheduled; start there
  2. For each listed worker, trace its dependsOn chain to find the edge that closes the loop (including a self-dependency) and remove or reverse it
  3. Verify every dependsOn entry matches the id of a worker actually present in the same workers array (watch for typos and stale ids after renames)
  4. If worker graphs are generated dynamically, run a cycle/unknown-dep check before handing them to the orchestrator

Example fix

// before
const workers = [
  { id: 'build', role: 'coder', dependsOn: ['test'] },
  { id: 'test', role: 'tester', dependsOn: ['build'] }, // build <-> test cycle
];

// after
const workers = [
  { id: 'build', role: 'coder', dependsOn: [] },
  { id: 'test', role: 'tester', dependsOn: ['build'] },
];
Defensive patterns

Strategy: validation

Validate before calling

function assertWorkersSchedulable(workers: WorkerConfig[]): void {
  const ids = new Set(workers.map((w) => w.id));
  for (const w of workers) {
    for (const dep of w.dependsOn ?? []) {
      if (!ids.has(dep)) throw new Error(`worker ${w.id} depends on unknown id ${dep}`);
    }
  }
  const placed = new Set<string>();
  let progress = true;
  while (progress) {
    progress = false;
    for (const w of workers) {
      if (placed.has(w.id)) continue;
      if ((w.dependsOn ?? []).every((d) => placed.has(d))) {
        placed.add(w.id);
        progress = true;
      }
    }
  }
  const remaining = workers.filter((w) => !placed.has(w.id)).map((w) => w.id);
  if (remaining.length) throw new Error(`dependency cycle among: ${remaining.join(', ')}`);
}

Try / catch

Catch Error around the orchestrator run; when the message starts with 'worker dependency cycle', parse the trailing id list and abort the swarm start — retrying without editing the dependsOn graph fails identically every time.

Prevention

When it happens

Trigger: Calling the orchestrator run/schedule path with a workers array where (1) two or more workers reference each other in dependsOn, (2) a worker lists its own id in dependsOn, or (3) a dependsOn entry references a typo'd, renamed, or removed worker id that does not exist in the same array.

Common situations: Copy-pasted worker configs where ids were renamed but dependsOn strings were not updated; programmatically generated fan-in/fan-out graphs that accidentally close a loop; merging two swarm configs whose workers each depend on the other's workers.

Related errors


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