Hmbown/CodeWhale · error

duplicate fleet worker id {}

Error message

duplicate fleet worker id {}

What it means

validate_task_spec_document iterates the top-level workers array (also accepted under the legacy alias worker_specs) and bails when a worker id is inserted into a BTreeSet twice. Worker ids address lease assignment and receipts, so duplicates are rejected before the run starts.

Source

Thrown at crates/tui/src/fleet/task_spec.rs:141

        }
        validate_fleet_name(&format!("task {} name", task.id), &task.name)?;
        if task.instructions.trim().is_empty() {
            bail!("fleet task {} instructions cannot be empty", task.id);
        }
        if let Some(objective) = &task.objective
            && objective.trim().is_empty()
        {
            bail!("fleet task {} objective cannot be empty", task.id);
        }
        validate_worker_profile(&task.id, task.worker.as_ref())?;
        validate_tags(&task.id, &task.tags)?;
        validate_workspace_requirements(task)?;
    }
    let mut worker_ids = BTreeSet::new();
    for worker in &doc.workers {
        validate_fleet_identity("worker id", &worker.id)?;
        if !worker_ids.insert(worker.id.clone()) {
            bail!("duplicate fleet worker id {}", worker.id);
        }
        validate_fleet_name(&format!("worker {} name", worker.id), &worker.name)?;
    }
    Ok(())
}

fn validate_fleet_identity(field: &str, value: &str) -> Result<()> {
    if value.is_empty() {
        bail!("fleet {field} cannot be empty");
    }
    if value.len() > MAX_FLEET_ID_BYTES || !value.chars().all(is_worker_token_char) {
        bail!(
            "fleet {field} must be a simple ASCII token no longer than {MAX_FLEET_ID_BYTES} bytes"
        );
    }
    Ok(())
}

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Find the duplicated worker id from the error and rename one entry
  2. Give generated workers sequential or role-based ids (builder-1, builder-2)
  3. When merging spec files, namespace worker ids by their source

Example fix

// before
"workers": [ { "id": "w1", "name": "a" }, { "id": "w1", "name": "b" } ]

// after
"workers": [ { "id": "w1", "name": "a" }, { "id": "w2", "name": "b" } ]
Defensive patterns

Strategy: validation

Validate before calling

fn worker_ids_unique(workers: &[FleetWorkerSpec]) -> bool {
    let mut seen = std::collections::BTreeSet::new();
    workers.iter().all(|w| seen.insert(w.id.as_str()))
}

Type guard

function hasUniqueWorkerIds(ws: { id: string }[]): boolean {
  return new Set(ws.map((w) => w.id)).size === ws.length;
}

Try / catch

if let Err(err) = load_task_spec_document(&path) {
    if let Some(id) = err.to_string().strip_prefix("duplicate fleet worker id ") {
        eprintln!("worker id '{id}' appears twice in {path:?}");
    }
    return Err(err);
}

Prevention

When it happens

Trigger: Two entries in the workers array sharing the same "id" field, regardless of their other fields differing.

Common situations: Scaling a spec by copy-pasting a worker block and forgetting to change the id; merging specs from teams that both used "worker-1"; generators emitting a constant id per worker.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/2388c79bb085ff95. Report an issue: GitHub.