Hmbown/CodeWhale · error

Fleet task ' ' path ' ' cannot contain parent traversal

Error message

Fleet task '{task_id}' {field} path '{}' cannot contain parent traversal

What it means

After component-level screening, normalize_fleet_relative_path splits the path on `/` and rejects any literal `..` segment — even ones that pass earlier checks — because a parent-traversal segment would let a write claim escape the workspace boundary. This is the defense-in-depth guard for Fleet write roots.

Solutions

  1. Remove the `..` segments and express the path from the repository root.
  2. List each target directory explicitly under writable_paths instead of traversing upward.
  3. If the target lies outside the workspace, move it inside or change the task's workspace.
  4. Trim empty/`.` segments first if the intent was a plain relative path like `./src` — write `src`.

Example fix

// before
writable_paths = ["src/../../shared/out"]

// after
writable_paths = ["shared/out"] // if actually in-repo, or restructure
Defensive patterns

Strategy: validation

Validate before calling

if p.to_string_lossy().split('/').any(|s| s == "..") {
    return Err("write paths cannot contain `..` segments");
}

Prevention

When it happens

Trigger: fleet_write_roots or fleet_runtime_write_roots passes a path whose segments include `..` after normalization (e.g. `src/../../escape` or `a/../b` residue), and the bail fires with the original path and the owning field name.

Common situations: Hand-written task specs using relative shorthands with `..`; generated configs concatenating prefixes that leave `..` segments; users trying to share one writable root across sibling repos via traversal.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

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

            matches!(
                component,
                std::path::Component::ParentDir
                    | std::path::Component::RootDir
                    | std::path::Component::Prefix(_)
            )
        })
    {
        bail!(
            "Fleet task '{task_id}' {field} path '{}' must be one repo-relative line and cannot escape the workspace",
            path.display()
        );
    }
    let mut segments = Vec::new();
    for segment in raw.split('/') {
        match segment {
            "" | "." => {}
            ".." => {
                bail!(
                    "Fleet task '{task_id}' {field} path '{}' cannot contain parent traversal",
                    path.display()
                );
            }
            value => segments.push(value),
        }
    }
    Ok(if segments.is_empty() {
        ".".to_string()
    } else {
        segments.join("/")
    })
}

fn fleet_coordination_contracts(task_spec: &FleetTaskSpec) -> Result<Vec<String>> {
    let Some(value) = task_spec.metadata.get("coordination_contracts") else {
        return Ok(Vec::new());
    };

View on GitHub (pinned to 73e0f67d83)