Hmbown/CodeWhale · error
sub-agent state path must include a parent directory
Error message
sub-agent state path must include a parent directory
What it means
`checked_subagent_state_path` requires a parent directory component after extracting the file name; parent() returning None means the path is degenerate (no directory part above the file). In practice this is nearly unreachable when paths are joined under an absolute state root, and it exists as a completeness guard.
Source
Thrown at crates/tui/src/tools/subagent/mod.rs:7683
&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)
}
fn normalize_subagent_workspace(workspace: &Path) -> PathBuf {
if let Ok(canonical) = workspace.canonicalize() {View on GitHub (pinned to 0c42157ee5)
Solutions
- Always derive sub-agent state paths via state_root.join(relative) rather than constructing bare Path values.
- Reject empty or component-less path strings at the input boundary.
Defensive patterns
Strategy: validation
Validate before calling
fn has_parent(rel: &str) -> bool {
std::path::Path::new(rel).parent().map_or(false, |p| !p.as_os_str().is_empty())
} Type guard
fn is_joinable_under_root(rel: &str) -> bool {
has_parent(rel) && has_file_name(rel)
} Try / catch
match checked_subagent_state_path(&state_root, Path::new(rel)) {
Err(e) if e.to_string().contains("parent directory") => { /* rebuild path under an explicit directory */ }
r => r?,
} Prevention
- Never construct state Path values by hand; always join under the state root.
When it happens
Trigger: A path object with a file name but no parent component — essentially only constructible from degenerate inputs (empty/odd PathBuf built outside the manager's join logic).
Common situations: Direct API use with hand-constructed Path values bypassing state_root.join; test code assembling unusual paths.
Related errors
- sub-agent state path must include a file name
- 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/5fa2823a5def88c0.
Report an issue: GitHub.