Hmbown/CodeWhale · error
state subdir must not be an absolute path
Error message
state subdir must not be an absolute path: {subdir} What it means
State subdirectories must be relative to the state root; an absolute subdir (e.g. `/tmp/x`, `C:\x`) would escape the state root entirely. `ensure_safe_state_subdir` rejects any subdir string that `Path::is_absolute()` accepts.
Solutions
- Pass only the relative component (e.g. `"sessions"`), not a full path
- Convert the absolute path to a path relative to the state root before passing it
- Use the API that accepts an explicit state-root override instead of smuggling it via subdir
Example fix
// before
let dir = state_dir_in("/home/u/.codewhale/sessions")?;
// after
let dir = state_dir_in("sessions")?; Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
fn is_relative_subdir(subdir: &str) -> bool {
!subdir.is_empty() && !Path::new(subdir).is_absolute()
} Type guard
fn safe_subdir(subdir: &str) -> Option<&str> {
(!subdir.is_empty() && !Path::new(subdir).is_absolute()).then_some(subdir)
} Try / catch
match state_dir_in(subdir) {
Err(e) if e.to_string().contains("absolute path") => {
// recompute as path relative to state root and retry
}
result => result,
} Prevention
- Split configured absolute state dirs into root + relative subdir
- Use `Path::strip_prefix` to derive relative components
- Never feed full filesystem paths into a subdir parameter
When it happens
Trigger: Calling a state-path helper with `subdir` starting with `/` (Unix) or containing a Windows drive/UNC prefix.
Common situations: Users setting a state-dir-like env var to an absolute path and passing it as the subdir; joining a configured absolute path into the subdir slot; copying a full path from another tool.
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 empty
- state subdir must not contain a root or prefix
- state subdir must not contain parent-dir (..) components
- 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/5c6368dde7261282.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/lib.rs:5727
/// Always returns the legacy path regardless of whether it exists.
pub fn legacy_deepseek_home() -> Result<PathBuf> {
codewhale_paths::legacy_deepseek_home().context("failed to resolve home directory")
}
/// Reject state subdirs that could escape the state root via path injection.
///
/// `ensure_state_dir` / `resolve_state_dir` are public APIs taking an arbitrary
/// subdir string; every in-tree caller passes a hardcoded single component
/// (e.g. `"sessions"`, `"."`). This validates defensively so a future caller
/// can never traverse out of the state root via `..` components or an absolute
/// path. Nested relative paths such as `"a/b"` are permitted.
fn ensure_safe_state_subdir(subdir: &str) -> Result<()> {
if subdir.is_empty() {
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(())
}
View on GitHub (pinned to 73e0f67d83)