{"record":{"id":"96ea409aa604c465","repo":"Hmbown/CodeWhale","slug":"failed-to-lock-settings-has-no-parent-director","errorCode":null,"errorMessage":"Failed to lock settings: {} has no parent directory","messagePattern":"Failed to lock settings: (.+?) has no parent directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/settings.rs","lineNumber":2311,"sourceCode":") -> Result<T> {\n    let path = Settings::path()?;\n    let _process_guard = lock_settings_transaction(settings_transaction_mutex(&path));\n    with_settings_file_lock(&path, || {\n        operation(&SettingsTransaction { path: path.clone() })\n    })\n}\n\n/// Hold an exclusive advisory lock on `<settings.toml>.lock` for `operation`.\n///\n/// The lock file is opened (not followed) with owner-only permissions and is\n/// created if absent. Dropping the `fd_lock` guard — including on an unwind —\n/// releases it, and the OS releases it if the process dies, so a crash cannot\n/// wedge another Codewhale instance out of its settings.\nfn with_settings_file_lock<T>(path: &Path, operation: impl FnOnce() -> Result<T>) -> Result<T> {\n    use std::fs;\n\n    let Some(parent) = path.parent().filter(|p| !p.as_os_str().is_empty()) else {\n        anyhow::bail!(\n            \"Failed to lock settings: {} has no parent directory\",\n            path.display()\n        );\n    };\n    fs::create_dir_all(parent)\n        .with_context(|| format!(\"Failed to create config directory {}\", parent.display()))?;\n\n    let mut lock_name = path\n        .file_name()\n        .context(\"Failed to lock settings: settings path has no file name\")?\n        .to_os_string();\n    lock_name.push(\".lock\");\n    let lock_path = parent.join(lock_name);\n    reject_settings_lock_symlink(&lock_path)?;\n\n    let mut options = fs::OpenOptions::new();\n    options.read(true).write(true).create(true);\n    #[cfg(unix)]","sourceCodeStart":2293,"sourceCodeEnd":2329,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/settings.rs#L2293-L2329","documentation":"with_settings_file_lock guards settings writes with a lockfile adjacent to the settings path. Before creating the lock it takes path.parent(); if the path has no parent component (e.g. a bare relative filename like \"settings.toml\" resolving to \"\") or the parent is the empty OS string, it bails instead of attempting to create a directory.","triggerScenarios":"Calling the settings load/save path with a PathBuf that has no parent — e.g. with_settings_file_lock(Path::new(\"settings.toml\"), ..), or constructing the settings path from an empty/unset config-dir variable so the resolved path is a bare filename.","commonSituations":"Environment misconfiguration where the config directory env/home lookup returns empty, unit tests passing a bare filename, or custom config-dir flags set to \"\".","solutions":["Pass an absolute settings path that includes a parent directory, e.g. ~/.config/codewhale/settings.toml.","Fix the misconfigured config-dir source (env var, CLI flag) so it is not empty — this codebase fails loud on misconfiguration by design.","In tests, use a tempdir path like std::env::temp_dir().join(\"settings.toml\")."],"exampleFix":"// before\nwith_settings_file_lock(Path::new(\"settings.toml\"), || settings.save())?;\n// after\nlet path = dirs::config_dir().unwrap().join(\"codewhale/settings.toml\");\nwith_settings_file_lock(&path, || settings.save())?;","handlingStrategy":"validation","validationCode":"fn has_parent(path: &std::path::Path) -> bool {\n    path.parent().map_or(false, |p| !p.as_os_str().is_empty())\n}","typeGuard":null,"tryCatchPattern":"match with_settings_file_lock(&path, || save_settings()) {\n    Err(e) if e.to_string().contains(\"has no parent directory\") => {\n        eprintln!(\"config dir unset; pass --config-dir or set the env var\");\n    }\n    r => r?,\n}","preventionTips":["Resolve the config directory (dirs::config_dir or equivalent) before building the settings path.","Fail fast when config-dir env/flags are empty instead of passing them through.","In tests, always join filenames under a tempdir."],"tags":["rust","filesystem","paths","locking"],"backgroundTag":"directory-not-found","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"}