ruvnet/ruflo · error
duplicate or empty task id
Error message
duplicate or empty task id: ${task.id} What it means
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.
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
Example fix
// before
const tasks = files.map(f => ({ id: path.basename(f), run: () => lint(f) }));
// two files named index.ts in different dirs -> duplicate id
// after
const tasks = files.map(f => ({ id: path.relative(root, f), run: () => lint(f) })); Defensive patterns
Strategy: validation
Validate before calling
const ids = tasks.map(t => t.id);
if (ids.some(id => !id) || new Set(ids).size !== ids.length) {
throw new Error('task ids must be non-empty and unique before runBoundedPool');
} Type guard
const haveUniqueIds = (tasks: readonly { id: string }[]): boolean => {
const seen = new Set<string>();
return tasks.every(t => Boolean(t.id) && !seen.has(t.id) && seen.add(t.id));
}; Prevention
- 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
When it happens
Trigger: 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').
Common situations: 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.
Related errors
- maxConcurrency must be a positive integer
- actualUsd must be a non-negative finite number
- Agent config must include id, name, and type
- agent must be an object
- anchor task has no query
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/048b240da14f463f.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/services/bounded-worker-pool.ts:36
/**
* Deterministic bounded worker pool for Codex/MetaHarness fanout.
*
* Completion order never affects result order. The caller-supplied AbortSignal
* and timeout cancel both queued and cooperative running work. No unbounded
* Promise.all is used.
*/
export async function runBoundedPool<T>(
tasks: readonly BoundedTask<T>[],
options: { maxConcurrency: number; timeoutMs?: number; signal?: AbortSignal },
): Promise<BoundedPoolResult<T>> {
const started = Date.now();
if (!Number.isInteger(options.maxConcurrency) || options.maxConcurrency < 1) {
throw new Error('maxConcurrency must be a positive integer');
}
const ids = new Set<string>();
for (const task of tasks) {
if (!task.id || ids.has(task.id)) throw new Error(`duplicate or empty task id: ${task.id}`);
ids.add(task.id);
}
const maxConcurrency = Math.min(options.maxConcurrency, tasks.length || 1);
const controller = new AbortController();
const onAbort = () => controller.abort(options.signal?.reason ?? new Error('cancelled'));
options.signal?.addEventListener('abort', onAbort, { once: true });
const timer = options.timeoutMs && options.timeoutMs > 0
? setTimeout(() => controller.abort(new Error('worker-pool-timeout')), options.timeoutMs)
: undefined;
const results = new Map<string, BoundedTaskResult<T>>();
let cursor = 0;
let active = 0;
let peakConcurrency = 0;
const worker = async (): Promise<void> => {
while (cursor < tasks.length) {
const taskIndex = cursor++;View on GitHub (pinned to fa13ee4ad6)