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
- Search the tasks array for the id in the error message and rename the duplicate occurrence to a unique value.
- If ids are generated, ensure the generator is deterministic and collision-free (e.g. include a counter or hash of the query).
- 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
- Run new Set(tasks.map(t => t.id)).size === tasks.length as a pre-commit check.
- Make id generators collision-free (include a counter or content hash).
- When duplicating a task as a template, always re-id it immediately.
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
- project flywheel anchor requires at least 4 labelled tasks
- invalid anchor task id at index ${index}
- anchor task ${task.id} has no query
- anchor task ${task.id} requires non-empty string labels
- flywheel anchor manifest requires path and sha256
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/96a65d128473c3e0.
Report an issue: GitHub.