Hmbown/CodeWhale · error

duplicate fleet task id {}

Error message

duplicate fleet task id {}

What it means

validate_task_spec_document collects task ids into a BTreeSet and bails when an id is inserted twice. Task ids are the join key between tasks, worker assignments, receipts and the ledger, so duplicates are rejected up front rather than resolved silently.

Source

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

        Some("toml") => toml::from_str::<FleetTaskSpecFile>(&raw)
            .with_context(|| format!("parsing TOML fleet task spec {}", path.display()))?,
        _ => serde_json::from_str::<FleetTaskSpecFile>(&raw)
            .with_context(|| format!("parsing JSON fleet task spec {}", path.display()))?,
    };
    let doc = parsed.into_document(fallback_name);
    validate_task_spec_document(&doc)?;
    Ok(doc)
}

pub fn validate_task_spec_document(doc: &FleetTaskSpecDocument) -> Result<()> {
    if doc.tasks.is_empty() {
        bail!("fleet task spec must include at least one task");
    }
    let mut ids = BTreeSet::new();
    for task in &doc.tasks {
        validate_fleet_identity("task id", &task.id)?;
        if !ids.insert(task.id.clone()) {
            bail!("duplicate fleet task id {}", task.id);
        }
        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()) {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Search the spec for the duplicated id shown in the error and rename one occurrence
  2. Adopt a naming scheme (feature-area-n) so generated specs cannot collide
  3. If merging specs, prefix ids with their source spec name

Example fix

// before
{ "tasks": [ { "id": "test", "name": "a", "instructions": "..." },
             { "id": "test", "name": "b", "instructions": "..." } ] }

// after
{ "tasks": [ { "id": "test-api", "name": "a", "instructions": "..." },
             { "id": "test-tui", "name": "b", "instructions": "..." } ] }
Defensive patterns

Strategy: validation

Validate before calling

fn task_ids_unique(tasks: &[FleetTaskSpec]) -> bool {
    let mut seen = std::collections::BTreeSet::new();
    tasks.iter().all(|t| seen.insert(t.id.as_str()))
}

Type guard

function hasUniqueIds(tasks: { id: string }[]): boolean {
  return new Set(tasks.map((t) => t.id)).size === tasks.length;
}

Try / catch

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

Prevention

When it happens

Trigger: Two entries in the tasks array with the same "id" field, e.g. two blocks both reading "id": "implement" after copy-paste.

Common situations: Copy-pasting a task block and editing everything but the id; generators that reset an id counter per section; merging two spec files that each used generic ids like "task-1".

Related errors


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