Hmbown/CodeWhale · error
sub-agent state path must include a file name
Error message
sub-agent state path must include a file name
What it means
`checked_subagent_state_path` requires the final path component to be a file name before joining it back onto the canonicalized parent. Paths that end in ".." or normalize to a bare root ("/") have no file_name(), and the guard rejects them before any filesystem access.
Source
Thrown at crates/tui/src/tools/subagent/mod.rs:7680
// is migrated on load (see load_state).
checked_subagent_state_path(
&state_root,
&Path::new(".codewhale")
.join("state")
.join(SUBAGENT_STATE_FILE),
)
}
fn checked_subagent_state_path(state_root: &Path, path: &Path) -> Result<PathBuf> {
let state_root = normalize_subagent_workspace(state_root);
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)
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Validate that the path's last non-dot component is a real file name before passing it in.
- Strip trailing separators and '..' tails, and reject paths with no file component outright.
- Build state paths as state_root.join(dir).join(file_name) with a non-empty file_name.
Example fix
// before
let p = format!("{}/..", agent_dir); // tail is '..', no file name
checked_subagent_state_path(root, Path::new(&p))?; // Err 1210
// after
let p = agent_dir.join(format!("{agent_id}.jsonl"));
checked_subagent_state_path(root, &p)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_file_name(rel: &str) -> bool {
std::path::Path::new(rel).file_name().is_some()
&& std::path::Path::new(rel).components().last()
.map_or(false, |c| matches!(c, std::path::Component::Normal(_)))
} Type guard
fn is_wellformed_state_path(rel: &str) -> bool {
has_file_name(rel) && !rel.ends_with("..")
} Try / catch
match checked_subagent_state_path(&state_root, Path::new(rel)) {
Err(e) if e.to_string().contains("must include a file name") => { /* reject input, request a concrete file path */ }
r => r?,
} Prevention
- Build state paths as root.join(dir).join(file_name) with non-empty parts.
- Sanitize model-supplied paths at the tool boundary before they reach state APIs.
When it happens
Trigger: A model- or user-supplied relative path whose tail is ".." (e.g. state/agents/..), or a constructed path that collapses to the root; typically input that was never sanitized into file.ext form.
Common situations: Agent tool calls echoing unsanitized paths; string-built paths from config with trailing separators or dot segments; templating that leaves an empty file component.
Related errors
- sub-agent state path must include a parent directory
- sub-agent transcript artifact must have a parent directory
- fleet task '{task_id}' {field} path '{}' must be one repo-re
- fleet task '{task_id}' {field} path '{}' cannot contain pare
- Runtime store root cannot contain '..' components
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/4ee4d527829d77a8.
Report an issue: GitHub.