Hmbown/CodeWhale · error
sub-agent state path must stay within state root: {}
Error message
sub-agent state path must stay within state root: {} What it means
`checked_subagent_state_path` canonicalizes (or component-normalizes) the parent directory, rejoins the file name, and requires the result to start with the normalized state root. This error means the fully resolved path escapes the state root — typically ".." traversal or an absolute parent outside the root. It is a fail-closed path-containment guard against model-supplied or tampered paths.
Source
Thrown at crates/tui/src/tools/subagent/mod.rs:7691
let absolute = if path.is_absolute() {
path.to_path_buf()
} else {
state_root.join(path)
};
let file_name = absolute
.file_name()
.ok_or_else(|| anyhow!("sub-agent state path must include a file name"))?;
let parent = absolute
.parent()
.ok_or_else(|| anyhow!("sub-agent state path must include a parent directory"))?;
let parent = match parent.canonicalize() {
Ok(parent) => parent,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => normalize_path_components(parent),
Err(err) => return Err(err.into()),
};
let state_path = parent.join(file_name);
if !state_path.starts_with(&state_root) {
return Err(anyhow!(
"sub-agent state path must stay within state root: {}",
state_path.display()
));
}
reject_root_relative_symlinks(&state_root, &state_path)?;
Ok(state_path)
}
fn normalize_subagent_workspace(workspace: &Path) -> PathBuf {
if let Ok(canonical) = workspace.canonicalize() {
return canonical;
}
let absolute = if workspace.is_absolute() {
workspace.to_path_buf()
} else {
std::env::current_dir()
.unwrap_or_else(|_| PathBuf::from("."))
.join(workspace)View on GitHub (pinned to 0c42157ee5)
Solutions
- Strip leading ../ and reject any relative path that cannot be contained under the state root before calling.
- Always pass paths relative to the manager's state root; never absolute external paths.
- Treat the error as hostile-input signal: audit where the path string came from rather than just fixing the path.
Example fix
// before
checked_subagent_state_path(root, Path::new("../../etc/passwd"))?; // Err 1212
// after: sanitize to a contained relative path
let rel = rel.replace('..', "_");
checked_subagent_state_path(root, &root.join(rel))?; Defensive patterns
Strategy: validation
Validate before calling
fn contained_rel_path(state_root: &Path, rel: &str) -> Option<PathBuf> {
let p = state_root.join(rel);
let norm = p.components().collect::<PathBuf>(); // squashes ..
norm.starts_with(state_root).then_some(norm)
} Type guard
fn is_contained(state_root: &Path, candidate: &Path) -> bool {
candidate.starts_with(state_root)
&& !candidate.components().any(|c| matches!(c, std::path::Component::ParentDir))
} Try / catch
match checked_subagent_state_path(&state_root, path) {
Err(e) if e.to_string().contains("within state root") => { /* treat as hostile input: reject and audit source */ }
r => r?,
} Prevention
- Only pass relative paths; resolve them under the manager's own state root.
- Strip or reject '..' segments at the input boundary.
- Treat escape attempts from model output as a security signal.
When it happens
Trigger: A relative path with ../ segments that resolves above the state root; an absolute path pointing elsewhere on disk; a parent that canonicalizes (via existing prefix) outside the root.
Common situations: Prompt-injected or hallucinated paths from a model; state dirs relocated while old absolute paths persisted; tools forwarding user input unfiltered.
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
- sub-agent state path must not traverse symlinks: {}
- persistent path allow rules must stay within the workspace
- config path cannot contain '..' components
- project workspace path cannot contain '..' components
- {kind} must be a single path component
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/5486c1195450cc41.
Report an issue: GitHub.