Hmbown/CodeWhale · error

Fleet task ' ' is write-capable but declares no…

Error message

Fleet task '{}' is write-capable but declares no workspace.writable_paths or metadata.coordination_contracts

What it means

fleet_task_to_worker_spec_with_profiles refuses to launch a write-capable Fleet worker that has neither workspace.writable_paths nor metadata.coordination_contracts. A worker with write permissions must declare a bounded claim — where it may write or which contracts it coordinates under — so mutations cannot be unbounded (see fleet_write_roots / fleet_coordination_contracts).

Solutions

  1. Add `workspace.writable_paths` entries (repo-relative paths) to the task spec.
  2. Add a `metadata.coordination_contracts` array (at most 16 string entries) to the task spec.
  3. Set permissions.write to false if the task should not write at all.
  4. Use an agent profile that contributes the bounded write claim for this role.

Example fix

// before
[tasks.refactor]
permissions.write = true

// after
[tasks.refactor]
permissions.write = true
[tasks.refactor.workspace]
writable_paths = ["src/refactor/"]
Defensive patterns

Strategy: validation

Validate before calling

if task.permissions.write && task.workspace.writable_paths.is_empty() && task.metadata.get("coordination_contracts").is_none() {
    return Err("write-capable task needs writable_paths or coordination_contracts");
}

Prevention

When it happens

Trigger: Building a worker spec from a FleetTaskSpec whose runtime_profile.permissions.write is true while fleet_write_roots(task_spec) returns an empty list and fleet_coordination_contracts(task_spec) returns an empty vec — i.e. no writable_paths in workspace config and no coordination_contracts metadata key.

Common situations: Defining a fleet task with a write-enabled role but forgetting `workspace.writable_paths`; copying a read-only task template and flipping permissions.write without adding a claim; profiles where the claim lives only in the parent profile and the task spec omits it.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/59d1bca522f399a9. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/worker_runtime.rs:453

    requested_runtime.provider = explicit_fleet_provider_id(agent_profile);
    if let Some(reasoning_effort) = effective_fleet_reasoning_effort(agent_profile) {
        requested_runtime.reasoning_effort = Some(reasoning_effort);
    }
    if let Some(agent_profile) = agent_profile
        && let Some(profile_depth) = agent_profile.profile.delegation.max_spawn_depth
    {
        requested_runtime.max_spawn_depth = requested_runtime.max_spawn_depth.min(profile_depth);
    }
    let runtime_profile = parent_runtime_profile
        .map(|parent| parent.derive_child(&requested_runtime))
        .unwrap_or(requested_runtime);
    let writable_roots = fleet_write_roots(task_spec)?;
    let coordination_contracts = fleet_coordination_contracts(task_spec)?;
    if runtime_profile.permissions.write
        && writable_roots.is_empty()
        && coordination_contracts.is_empty()
    {
        bail!(
            "Fleet task '{}' is write-capable but declares no workspace.writable_paths or metadata.coordination_contracts",
            task_spec.id
        );
    }
    let session_name = format!("fleet-{}-{}", worker_id, task_spec.id);
    let launch_manifest = ChildLaunchManifest {
        owner_session: run_id.to_string(),
        child_id: worker_id.to_string(),
        profile: runtime_profile.clone(),
        prompt: objective.clone(),
        cwd: Some(workspace.display().to_string()),
        worktree: worker_workspace_is_isolated(coordination_workspace, workspace),
        writable_roots,
        writable_files: Vec::new(),
        coordination_contracts,
        expected_artifact: None,
        deliverables: Vec::new(),
        resume_identity: Some(session_name.clone()),

View on GitHub (pinned to 73e0f67d83)