ruvnet/ruflo · error · Error

duplicate anchor task id: ${task.id}

Error message

duplicate anchor task id: ${task.id}

What it means

Thrown during anchor parsing when two tasks in the tasks array share the same id. Ids are tracked in a Set as the loop proceeds, so the second occurrence is rejected. Duplicate ids would corrupt downstream evaluation that keys results by task id, so this is enforced as a hard uniqueness constraint.

Source

Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:91

function parseTasks(path: string): { version: string; tasks: HumanEvalTask[] } {
  const parsed = JSON.parse(readFileSync(path, 'utf8')) as {
    schemaVersion?: string;
    version?: string;
    tasks?: HumanEvalTask[];
  };
  if (parsed.schemaVersion && parsed.schemaVersion !== PROJECT_ANCHOR_SCHEMA) {
    throw new Error(`unsupported flywheel anchor schema: ${parsed.schemaVersion}`);
  }
  if (!Array.isArray(parsed.tasks) || parsed.tasks.length < 4) {
    throw new Error('project flywheel anchor requires at least 4 labelled tasks');
  }
  const ids = new Set<string>();
  for (const [index, task] of parsed.tasks.entries()) {
    if (!task || typeof task.id !== 'string' || !/^[A-Za-z0-9._-]{1,128}$/.test(task.id)) {
      throw new Error(`invalid anchor task id at index ${index}`);
    }
    if (ids.has(task.id)) throw new Error(`duplicate anchor task id: ${task.id}`);
    ids.add(task.id);
    if (typeof task.q !== 'string' || task.q.trim().length === 0) {
      throw new Error(`anchor task ${task.id} has no query`);
    }
    if (!Array.isArray(task.labels) || task.labels.length === 0 || task.labels.some((label) => typeof label !== 'string' || !label.trim())) {
      throw new Error(`anchor task ${task.id} requires non-empty string labels`);
    }
  }
  return { version: parsed.version ?? 'project-anchor-v1', tasks: parsed.tasks };
}

function toSelection(
  path: string,
  expectedHash: string,
): FlywheelAnchorSelection {
  const parsed = parseTasks(path);
  const actualHash = humanEvalHash(parsed.tasks);
  if (actualHash !== normalizeHash(expectedHash)) {

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Search the tasks array for the id in the error message and rename the duplicate occurrence to a unique value.
  2. If ids are generated, ensure the generator is deterministic and collision-free (e.g. include a counter or hash of the query).
  3. Run a quick dedup check: new Set(tasks.map(t => t.id)).size === tasks.length before saving the anchor.

Example fix

// before
[{"id":"auth","q":"...","labels":["x"]}, {"id":"auth","q":"...","labels":["y"]}]
// after — make ids unique
[{"id":"auth-login","q":"...","labels":["x"]}, {"id":"auth-logout","q":"...","labels":["y"]}]
Defensive patterns

Strategy: validation

Validate before calling

function assertUniqueIds(tasks: { id: string }[]): void {
  const ids = tasks.map(t => t.id);
  const dup = ids.find((id, i) => ids.indexOf(id) !== i);
  if (dup) throw new Error(`duplicate task id detected before load: ${dup}`);
}

Type guard

function hasUniqueIds(tasks: { id: string }[]): boolean {
  const seen = new Set<string>();
  for (const t of tasks) {
    if (seen.has(t.id)) return false;
    seen.add(t.id);
  }
  return true;
}

Try / catch

try {
  loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
  if (e instanceof Error && /duplicate anchor task id: (.+)$/.test(e.message)) {
    const dupId = e.message.split(': ').pop();
    // rename the second occurrence of dupId in the anchor, then retry
  }
  throw e;
}

Prevention

When it happens

Trigger: An anchor file containing two task objects with an identical id string; copy-pasting a task and forgetting to change its id; an id-generation function that produced a collision.

Common situations: Duplicating a template task to reach the 4-task minimum without re-id'ing; merging two anchor files that both used generic ids like "task-1"; an id normalizer that collapsed two distinct ids to the same sanitized form.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/96a65d128473c3e0. Report an issue: GitHub.