Hmbown/CodeWhale · error

fleet task spec must include at least one task

Error message

fleet task spec must include at least one task

What it means

load_task_spec_document accepts three shapes: a full document with a tasks array, a bare JSON array of tasks, or a single task object. validate_task_spec_document then requires at least one task; tasks is serde-defaulted to an empty Vec, so a document that omits it or sets tasks = [] fails here. A fleet run with zero tasks is meaningless, so the spec is rejected before any worker spawns.

Source

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

        .file_stem()
        .and_then(|s| s.to_str())
        .filter(|s| !s.is_empty())
        .unwrap_or("fleet-run")
        .to_string();
    let parsed = match path.extension().and_then(|s| s.to_str()) {
        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)?;

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Add at least one task object to the tasks array with id, name and instructions
  2. If you meant a single task, the bare-array or single-object forms also satisfy the check
  3. Verify with a JSON/TOML linter that the tasks key survived your pipeline

Example fix

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

// after
{
  "name": "run",
  "workers": [{ "id": "w1", "name": "w1" }],
  "tasks": [{ "id": "t1", "name": "t1", "instructions": "Run the suite" }]
}
Defensive patterns

Strategy: validation

Validate before calling

// after parsing, before starting the run:
if doc.tasks.is_empty() {
    return Err(anyhow!("fleet task spec must include at least one task"));
}

Type guard

// TypeScript, when authoring specs:
function hasTasks(v: unknown): v is { tasks: unknown[] } {
  return (
    !!v && typeof v === "object" && Array.isArray((v as any).tasks) &&
    (v as any).tasks.length > 0
  );
}

Try / catch

if let Err(err) = load_task_spec_document(&path) {
    if err.to_string().contains("at least one task") {
        eprintln!("{path:?} has no tasks - add one or use the bare-array form");
    }
    return Err(err);
}

Prevention

When it happens

Trigger: A JSON spec {"workers":[...]} with no tasks key; {"tasks":[]}; a TOML spec with tasks = []; a YAML-to-JSON conversion that dropped the tasks array.

Common situations: Drafting a spec workers-first and forgetting tasks; templates with a placeholder empty array; scripts that emit the document skeleton before tasks are generated.

Related errors


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