Hmbown/CodeWhale · error

state subdir must not be empty

Error message

state subdir must not be empty

What it means

`ensure_safe_state_subdir` defensively validates the state subdirectory string used to locate state under the state root. An empty subdir is rejected because it is not a valid single relative path component and would silently resolve to the state root itself.

Solutions

  1. Pass a non-empty relative subdir such as `"sessions"`
  2. Provide a default (`"sessions"`) when the config/env value is empty
  3. If the state root itself is intended, call the root-path API instead of the subdir one

Example fix

// before
let dir = state_dir_in("")?;
// after
let dir = state_dir_in("sessions")?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_subdir(subdir: &str) -> bool { !subdir.is_empty() }

Try / catch

match state_dir_in(subdir) {
    Err(e) if e.to_string().contains("subdir must not be empty") => {
        state_dir_in("sessions")
    }
    result => result,
}

Prevention

When it happens

Trigger: Calling a state-path helper that routes through `ensure_safe_state_subdir` with `subdir = ""`. In-tree callers pass hardcoded values like `"sessions"` or `"."`, so this fires only with an empty string from a caller-built path.

Common situations: Building the subdir from an env var or config value that is unset/empty; a format/trim bug dropping the segment; forwarding an empty `--state-subdir` style CLI flag.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/1fc4139ff0011dd1. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/lib.rs:5723

}

/// Resolve the legacy DeepSeek home directory (`$HOME/.deepseek`).
///
/// 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}");

View on GitHub (pinned to 73e0f67d83)