Hmbown/CodeWhale · error
state subdir must not contain parent-dir (..) components
Error message
state subdir must not contain parent-dir (..) components: {subdir} What it means
Parent-directory (`..`) components in a state subdir would let callers traverse out of the state root. `ensure_safe_state_subdir` rejects any subdir containing `Component::ParentDir`, even nested relative paths like `"a/b"` remain allowed.
Solutions
- Use only `Normal` path components relative to the state root (nested `"a/b"` is fine)
- Canonicalize the target and recompute it relative to the state root before calling
- Use the explicit state-root override API if a different root location is genuinely needed
Example fix
// before
let dir = state_dir_in("../other-tool-state")?;
// after
let dir = state_dir_in("sessions")?; Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Component, Path};
fn no_parent_components(subdir: &str) -> bool {
!Path::new(subdir).components()
.any(|c| matches!(c, Component::ParentDir))
} Type guard
fn contained_subdir(subdir: &str) -> Option<&str> {
let p = Path::new(subdir);
if subdir.is_empty()
|| p.components().any(|c| !matches!(c, Component::Normal(_)))
{ None } else { Some(subdir) }
} Try / catch
match state_dir_in(subdir) {
Err(e) if e.to_string().contains("parent-dir") => {
// reject or recompute the path within the state root
}
result => result,
} Prevention
- Validate that every component is `Component::Normal` before calling
- Canonicalize user paths and verify containment in the state root
- Never interpolate user input into subdir strings
When it happens
Trigger: Calling a state-path helper with a subdir containing `..` segments, e.g. `"../shared"` or `"sessions/../../etc"`.
Common situations: Constructing paths with `..` to reach a sibling directory of the state root; user-supplied relative paths passed through unvalidated; template strings with `..` placeholders left unsubstituted.
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
- state subdir must not be an absolute path
- state subdir must not be empty
- state subdir must not contain a root or prefix
- budget baseline_receipt path changed
- Codewhale credentials directory has an unsupported component
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/2c09ec007feb71ac.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/lib.rs:5741
bail!("state subdir must not be empty");
}
let path = std::path::Path::new(subdir);
if path.is_absolute() {
bail!("state subdir must not be an absolute path: {subdir}");
}
if path.components().any(|c| {
matches!(
c,
std::path::Component::RootDir | std::path::Component::Prefix(_)
)
}) {
bail!("state subdir must not contain a root or prefix: {subdir}");
}
if path
.components()
.any(|c| matches!(c, std::path::Component::ParentDir))
{
bail!("state subdir must not contain parent-dir (..) components: {subdir}");
}
Ok(())
}
/// Resolve a state subdirectory, preferring the CodeWhale root if
/// it already exists, otherwise falling back to the legacy root.
///
/// This is the read-path resolver: it returns the primary path when
/// migration has occurred or on a fresh install, but keeps reading
/// from the legacy path for users who haven't migrated yet.
pub fn resolve_state_dir(subdir: &str) -> Result<PathBuf> {
ensure_safe_state_subdir(subdir)?;
let explicit_codewhale_home = codewhale_home_is_explicit();
let primary = codewhale_home()?.join(subdir);
if explicit_codewhale_home || primary.exists() {
return Ok(primary);
}
let legacy = legacy_deepseek_home()?.join(subdir);View on GitHub (pinned to 73e0f67d83)