{"record":{"id":"1fc4139ff0011dd1","repo":"Hmbown/CodeWhale","slug":"state-subdir-must-not-be-empty","errorCode":null,"errorMessage":"state subdir must not be empty","messagePattern":"state subdir must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/lib.rs","lineNumber":5723,"sourceCode":"}\n\n/// Resolve the legacy DeepSeek home directory (`$HOME/.deepseek`).\n///\n/// Always returns the legacy path regardless of whether it exists.\npub fn legacy_deepseek_home() -> Result<PathBuf> {\n    codewhale_paths::legacy_deepseek_home().context(\"failed to resolve home directory\")\n}\n\n/// Reject state subdirs that could escape the state root via path injection.\n///\n/// `ensure_state_dir` / `resolve_state_dir` are public APIs taking an arbitrary\n/// subdir string; every in-tree caller passes a hardcoded single component\n/// (e.g. `\"sessions\"`, `\".\"`). This validates defensively so a future caller\n/// can never traverse out of the state root via `..` components or an absolute\n/// path. Nested relative paths such as `\"a/b\"` are permitted.\nfn ensure_safe_state_subdir(subdir: &str) -> Result<()> {\n    if subdir.is_empty() {\n        bail!(\"state subdir must not be empty\");\n    }\n    let path = std::path::Path::new(subdir);\n    if path.is_absolute() {\n        bail!(\"state subdir must not be an absolute path: {subdir}\");\n    }\n    if path.components().any(|c| {\n        matches!(\n            c,\n            std::path::Component::RootDir | std::path::Component::Prefix(_)\n        )\n    }) {\n        bail!(\"state subdir must not contain a root or prefix: {subdir}\");\n    }\n    if path\n        .components()\n        .any(|c| matches!(c, std::path::Component::ParentDir))\n    {\n        bail!(\"state subdir must not contain parent-dir (..) components: {subdir}\");","sourceCodeStart":5705,"sourceCodeEnd":5741,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/lib.rs#L5705-L5741","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a non-empty relative subdir such as `\"sessions\"`","Provide a default (`\"sessions\"`) when the config/env value is empty","If the state root itself is intended, call the root-path API instead of the subdir one"],"exampleFix":"// before\nlet dir = state_dir_in(\"\")?;\n// after\nlet dir = state_dir_in(\"sessions\")?;","handlingStrategy":"validation","validationCode":"fn valid_subdir(subdir: &str) -> bool { !subdir.is_empty() }","typeGuard":null,"tryCatchPattern":"match state_dir_in(subdir) {\n    Err(e) if e.to_string().contains(\"subdir must not be empty\") => {\n        state_dir_in(\"sessions\")\n    }\n    result => result,\n}","preventionTips":["Never pass user/env-supplied strings directly as subdir without a non-empty check","Default to a hardcoded component like `\"sessions\"` when unset","Keep subdir values hardcoded at call sites as the module intends"],"tags":["path","validation","state"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}