{"record":{"id":"971081540886ae97","repo":"Hmbown/CodeWhale","slug":"fleet-task-task-id-field-path-must-be-one-repo-relative-line","errorCode":null,"errorMessage":"Fleet task '{task_id}' {field} path '{}' must be one repo-relative line and cannot escape the workspace","messagePattern":"Fleet task '(.+?)' (.+?) path '(.+?)' must be one repo-relative line and cannot escape the workspace","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/worker_runtime.rs","lineNumber":562,"sourceCode":"\nfn normalize_fleet_relative_path(\n    path: &std::path::Path,\n    task_id: &str,\n    field: &str,\n) -> Result<String> {\n    let raw = path.to_string_lossy().replace('\\\\', \"/\");\n    if raw.chars().any(|ch| matches!(ch, '\\0' | '\\r' | '\\n'))\n        || path.is_absolute()\n        || path.components().any(|component| {\n            matches!(\n                component,\n                std::path::Component::ParentDir\n                    | std::path::Component::RootDir\n                    | std::path::Component::Prefix(_)\n            )\n        })\n    {\n        bail!(\n            \"Fleet task '{task_id}' {field} path '{}' must be one repo-relative line and cannot escape the workspace\",\n            path.display()\n        );\n    }\n    let mut segments = Vec::new();\n    for segment in raw.split('/') {\n        match segment {\n            \"\" | \".\" => {}\n            \"..\" => {\n                bail!(\n                    \"Fleet task '{task_id}' {field} path '{}' cannot contain parent traversal\",\n                    path.display()\n                );\n            }\n            value => segments.push(value),\n        }\n    }\n    Ok(if segments.is_empty() {","sourceCodeStart":544,"sourceCodeEnd":580,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/fleet/worker_runtime.rs#L544-L580","documentation":"normalize_fleet_relative_path rejects a declared Fleet write path that is not a clean repo-relative path: if the raw path contains a ParentDir, RootDir, or Prefix component (absolute path, drive prefix, or leading `..`), it cannot be contained inside the workspace and the task fails. Write claims must each be a single repo-relative line so the runtime can bound them under the workspace root.","triggerScenarios":"fleet_write_roots or fleet_runtime_write_roots calls normalize_fleet_relative_path with a path like `/etc/passwd`, `C:\\repo\\src`, `../sibling/`, or `../../out`, producing this error with field naming which claim (writable_paths or runtime write root) was rejected.","commonSituations":"Pasting an absolute path from the host into writable_paths; using `..` to reach a sibling repo from a fleet task; Windows-authored configs with drive-letter prefixes checked into a repo used on Linux.","solutions":["Replace the absolute path with a path relative to the repository root (e.g. `src/module/`).","Remove any `..` traversal; enumerate the actual in-repo directories you need instead.","Strip drive-letter/UNC prefixes from Windows-style entries and re-express them relative to the workspace.","If you truly need out-of-workspace writes, restructure the task or workspace rather than escaping the claim."],"exampleFix":"// before\nwritable_paths = [\"/home/me/repo/src\", \"../shared\"]\n\n// after\nwritable_paths = [\"src\"]","handlingStrategy":"validation","validationCode":"fn is_clean_relative(p: &Path) -> bool {\n    !p.is_absolute()\n        && p.components().all(|c| !matches!(c, Component::ParentDir | Component::RootDir | Component::Prefix(_)))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Author writable_paths strictly relative to the repo root.","Never use absolute or Windows-style paths in fleet configs.","Lint task specs for `..` and leading `/` before submission."],"tags":["fleet","path-validation","security","workspace"],"backgroundTag":"path-traversal-blocked","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}