{"record":{"id":"048b240da14f463f","repo":"ruvnet/ruflo","slug":"duplicate-or-empty-task-id-task-id","errorCode":null,"errorMessage":"duplicate or empty task id: ${task.id}","messagePattern":"duplicate or empty task id: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/bounded-worker-pool.ts","lineNumber":36,"sourceCode":"\n/**\n * Deterministic bounded worker pool for Codex/MetaHarness fanout.\n *\n * Completion order never affects result order. The caller-supplied AbortSignal\n * and timeout cancel both queued and cooperative running work. No unbounded\n * Promise.all is used.\n */\nexport async function runBoundedPool<T>(\n  tasks: readonly BoundedTask<T>[],\n  options: { maxConcurrency: number; timeoutMs?: number; signal?: AbortSignal },\n): Promise<BoundedPoolResult<T>> {\n  const started = Date.now();\n  if (!Number.isInteger(options.maxConcurrency) || options.maxConcurrency < 1) {\n    throw new Error('maxConcurrency must be a positive integer');\n  }\n  const ids = new Set<string>();\n  for (const task of tasks) {\n    if (!task.id || ids.has(task.id)) throw new Error(`duplicate or empty task id: ${task.id}`);\n    ids.add(task.id);\n  }\n  const maxConcurrency = Math.min(options.maxConcurrency, tasks.length || 1);\n  const controller = new AbortController();\n  const onAbort = () => controller.abort(options.signal?.reason ?? new Error('cancelled'));\n  options.signal?.addEventListener('abort', onAbort, { once: true });\n  const timer = options.timeoutMs && options.timeoutMs > 0\n    ? setTimeout(() => controller.abort(new Error('worker-pool-timeout')), options.timeoutMs)\n    : undefined;\n\n  const results = new Map<string, BoundedTaskResult<T>>();\n  let cursor = 0;\n  let active = 0;\n  let peakConcurrency = 0;\n\n  const worker = async (): Promise<void> => {\n    while (cursor < tasks.length) {\n      const taskIndex = cursor++;","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/services/bounded-worker-pool.ts#L18-L54","documentation":"runBoundedPool requires every BoundedTask to carry a non-empty, unique id: results are collected into a Map keyed by id and completion order must not affect result order, so a duplicate id would silently overwrite one task's result and an empty id is unusable as a key. Validation runs over all tasks up front, before any work starts, and the error names the offending id.","triggerScenarios":"Building tasks with .map((x) => ({ id: x.name, ... })) where names repeat; reusing a constant id string; generating ids from array indices after filtering/deduping that leaves duplicates; forgetting to set id so it is undefined (falsy -> reported as 'empty').","commonSituations":"Fanout over work items keyed by non-unique fields (file basenames, issue titles), merging task lists from multiple sources without re-keying, refactors that renamed the id field and left it unset.","solutions":["Give each task a structurally unique id, e.g. `${source}:${name}` or crypto.randomUUID()","If uniqueness across sources cannot be guaranteed, prefix by source before merging","Assert on duplicates before calling: new Set(ids).size === tasks.length"],"exampleFix":"// before\nconst tasks = files.map(f => ({ id: path.basename(f), run: () => lint(f) }));\n// two files named index.ts in different dirs -> duplicate id\n\n// after\nconst tasks = files.map(f => ({ id: path.relative(root, f), run: () => lint(f) }));","handlingStrategy":"validation","validationCode":"const ids = tasks.map(t => t.id);\nif (ids.some(id => !id) || new Set(ids).size !== ids.length) {\n  throw new Error('task ids must be non-empty and unique before runBoundedPool');\n}","typeGuard":"const haveUniqueIds = (tasks: readonly { id: string }[]): boolean => {\n  const seen = new Set<string>();\n  return tasks.every(t => Boolean(t.id) && !seen.has(t.id) && seen.add(t.id));\n};","tryCatchPattern":null,"preventionTips":["Build ids from structurally unique keys (full relative path, uuid) rather than names or filtered indices","When merging task lists from multiple sources, prefix ids with the source name","Add a unit test that fans out over fixtures with duplicate basenames"],"tags":["worker-pool","unique-id","validation","deduplication"],"backgroundTag":"duplicate-key","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}