{"record":{"id":"eb8790ecdb1ecf55","repo":"sigoden/aichat","slug":"failed-to-write-to-no-parent-path","errorCode":null,"errorMessage":"Failed to write to '{}', No parent path","messagePattern":"Failed to write to '(.+?)', No parent path","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/config/mod.rs","lineNumber":2665,"sourceCode":"    #[cfg(unix)]\n    {\n        use std::os::unix::prelude::PermissionsExt;\n        let perms = std::fs::Permissions::from_mode(0o600);\n        std::fs::set_permissions(config_path, perms)?;\n    }\n\n    println!(\"✓ Saved the config file to '{}'.\\n\", config_path.display());\n\n    Ok(())\n}\n\npub(crate) fn ensure_parent_exists(path: &Path) -> Result<()> {\n    if path.exists() {\n        return Ok(());\n    }\n    let parent = path\n        .parent()\n        .ok_or_else(|| anyhow!(\"Failed to write to '{}', No parent path\", path.display()))?;\n    if !parent.exists() {\n        create_dir_all(parent).with_context(|| {\n            format!(\n                \"Failed to write to '{}', Cannot create parent directory\",\n                path.display()\n            )\n        })?;\n    }\n    Ok(())\n}\n\nfn read_env_value<T>(key: &str) -> Option<Option<T>>\nwhere\n    T: std::str::FromStr,\n{\n    let value = env::var(key).ok()?;\n    let value = parse_value(&value).ok()?;\n    Some(value)","sourceCodeStart":2647,"sourceCodeEnd":2683,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/config/mod.rs#L2647-L2683","documentation":"ensure_parent_exists validates that a path can be written: if the path itself doesn't exist, it needs a parent directory to create. Paths with no parent component (e.g. a bare relative filename like 'foo.txt' resolving to an empty parent, or a root-level path edge case) fail this check before any write is attempted.","triggerScenarios":"Calling ensure_parent_exists (directly or via a config/file write path) with a Path that has no parent component and does not already exist.","commonSituations":"Passing a bare filename without a directory component where the code expects 'dir/file'; constructing paths from user input that strip the directory; unusual relative paths like './' mis-parsed.","solutions":["Pass a path that includes a directory component, e.g. './foo.txt' instead of 'foo.txt'","Create the parent directory yourself before calling","Check the path construction logic so the target file always sits inside an explicit directory"],"exampleFix":"// before\nensure_parent_exists(Path::new(\"config.yaml\"))?\n// after\nensure_parent_exists(Path::new(\"./config.yaml\"))?","handlingStrategy":"validation","validationCode":"fn writable(path: &Path) -> bool {\n    path.exists() || path.parent().map(|p| !p.as_os_str().is_empty()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match ensure_parent_exists(path) {\n    Ok(()) => write(path, data)?,\n    Err(e) if e.to_string().contains(\"No parent path\") => {\n        let p = Path::new(\"./\").join(path);\n        ensure_parent_exists(&p)?; write(&p, data)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always build target paths with an explicit directory component","Prefer PathBuf::from(\".\").join(name) over bare filenames","Normalize user-supplied paths before writing"],"tags":["filesystem","path","io"],"backgroundTag":"file-write-failed","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}