Hmbown/CodeWhale · error
skill version marker should have a parent directory
Error message
skill version marker should have a parent directory
What it means
Panic in the atomic skill version-marker writer. `Path::parent()` returns `None` only when the path is empty or the filesystem root (`/`), so the expect fires when the composed marker path degenerated to `""` or `"/"` — e.g. an empty state-dir base joined with nothing — before the `NamedTempFile::new_in` call can run.
Source
Thrown at crates/tui/src/skills/system.rs:474
/// are preserved.
fn retire_unchanged_v4_best_practices(skills_dir: &Path) -> std::io::Result<bool> {
let dir = skills_dir.join("v4-best-practices");
let file = dir.join("SKILL.md");
if !file.exists() {
return Ok(false);
}
let existing = fs::read_to_string(&file)?;
if existing != v4_best_practices_body() {
return Ok(false);
}
fs::remove_dir_all(&dir)?;
Ok(true)
}
fn write_marker_atomically(marker: &Path, version: &str) -> std::io::Result<()> {
let parent = marker
.parent()
.expect("skill version marker should have a parent directory");
let mut temporary = tempfile::NamedTempFile::new_in(parent)?;
temporary.write_all(version.as_bytes())?;
temporary.as_file().sync_all()?;
// `rename` atomically replaces a file on Unix. Windows refuses to replace
// an existing destination, so remove only this reserved marker first.
#[cfg(windows)]
if marker.exists() {
fs::remove_file(marker)?;
}
fs::rename(temporary.path(), marker)
}
#[cfg(test)]
mod tests;
View on GitHub (pinned to 0c42157ee5)
Solutions
- Trace where the marker path is composed (skills state dir) and guard: reject empty or root paths before calling `write_marker_atomically`.
- Fix the override/config value that produced the degenerate path.
- Return `Err(io::ErrorKind::InvalidInput)` naming the bad path instead of `expect`.
- Add a test with an empty marker path asserting the graceful error.
Example fix
// before
let parent = marker.parent().expect("skill version marker should have a parent directory");
// after: reject degenerate paths with a real error
let Some(parent) = marker.parent() else {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("marker path `{}` has no parent directory", marker.display()),
));
}; Defensive patterns
Strategy: validation
Validate before calling
fn marker_path_ok(marker: &std::path::Path) -> bool {
!marker.as_os_str().is_empty()
&& marker != std::path::Path::new("/")
&& marker.parent().is_some()
} Prevention
- Never build marker paths from unvalidated empty strings or env overrides.
- Unit-test the path composition for the skills state dir, including override values.
When it happens
Trigger: A marker path built from an empty string (misconfigured/empty state-dir override, `PathBuf::from("")` flowing through) or literally `"/"`; the first skill-system versioning write then panics at system.rs:474.
Common situations: Env/config overrides for the codewhale state dir set to empty; tests constructing marker paths from `String::new()`; path-joining code that drops the filename component under some condition.
Related errors
- hardcoded project notes state path is valid
- hardcoded project MCP state path is valid
- Runtime store root cannot contain '..' components
- Failed to promote staged task {}: {promote_err}
- validated sandbox permission
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/7bbea85718956046.
Report an issue: GitHub.