Hmbown/CodeWhale · error
fleet task '{task_id}' {field} path '{}' must be one repo-re
Error message
fleet task '{task_id}' {field} path '{}' must be one repo-relative line and cannot escape the workspace What it means
A path in `workspace.writable_paths` failed `normalize_fleet_relative_path`: it must be a single repo-relative line. Rejected shapes are absolute paths (leading `/`, Windows drive prefixes, root-dir components), any `..` component, and embedded NUL/CR/LF characters. Backslashes are treated as separators (they are replaced with `/` first), so Windows-style paths do not slip through on Unix.
Source
Thrown at crates/tui/src/fleet/worker_runtime.rs:353
fn normalize_fleet_relative_path(
path: &std::path::Path,
task_id: &str,
field: &str,
) -> Result<String> {
let raw = path.to_string_lossy().replace('\\', "/");
if raw.chars().any(|ch| matches!(ch, '\0' | '\r' | '\n'))
|| path.is_absolute()
|| path.components().any(|component| {
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() {View on GitHub (pinned to 0c42157ee5)
Solutions
- Express the path relative to the workspace root, e.g. "src" or "pkg-a/src".
- Remove drive letters, leading slashes, and any newline characters from the value.
- For paths outside the repo, use a coordination mechanism instead of an absolute writable path — the sandbox deliberately forbids escapes.
Example fix
# before [workspace] writable_paths = ["/home/me/repo/src", "C:\\work\\pkg"] # after [workspace] writable_paths = ["src", "pkg"]
Defensive patterns
Strategy: validation
Validate before calling
fn is_safe_relative_path(raw: &str) -> bool {
let normalized = raw.replace('\\', "/");
!normalized.chars().any(|c| matches!(c, '\0' | '\r' | '\n'))
&& !normalized.starts_with('/')
&& !normalized.split('/').any(|seg| seg == ".." || seg.contains(':'))
&& !normalized.trim().is_empty()
} Type guard
fn is_workspace_relative(path: &std::path::Path) -> bool {
let raw = path.to_string_lossy().replace('\\', "/");
!path.is_absolute() && !raw.split('/').any(|seg| seg == "..")
} Prevention
- Always author writable_paths relative to the workspace root; never absolute or drive-prefixed.
- Canonicalize externally sourced paths, then re-anchor them under the workspace before putting them in a spec.
- Reject multi-line values in path fields at template-render time.
When it happens
Trigger: `writable_paths = ["/tmp/work"]` (absolute), `["C:\\repo\\src"]` (Windows prefix), `["src\\nother"]` or a value containing a literal newline, or any entry with a `..` component.
Common situations: Authors pasting absolute output directories from their shell; specs written on Windows using backslash paths; templating that embeds multi-line values into a path field.
Related errors
- fleet task '{task_id}' {field} path '{}' cannot contain pare
- SSH Fleet workers do not yet support nested workspace.root v
- fleet task '{}' is write-capable but declares no workspace.w
- persistent allow rules must be scoped to a workspace
- project workspace path cannot be empty
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/ff0b552f1b5db428.
Report an issue: GitHub.