Hmbown/CodeWhale · error
Failed to lock settings
Error message
Failed to lock settings: {} has no parent directory What it means
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.
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").
Example fix
// before
with_settings_file_lock(Path::new("settings.toml"), || settings.save())?;
// after
let path = dirs::config_dir().unwrap().join("codewhale/settings.toml");
with_settings_file_lock(&path, || settings.save())?; Defensive patterns
Strategy: validation
Validate before calling
fn has_parent(path: &std::path::Path) -> bool {
path.parent().map_or(false, |p| !p.as_os_str().is_empty())
} Try / catch
match with_settings_file_lock(&path, || save_settings()) {
Err(e) if e.to_string().contains("has no parent directory") => {
eprintln!("config dir unset; pass --config-dir or set the env var");
}
r => r?,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Environment misconfiguration where the config directory env/home lookup returns empty, unit tests passing a bare filename, or custom config-dir flags set to "".
Related errors
- approval log has no parent
- commit
- config lock was redirected while opening
- create isolated home
- credential fixture
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/96ea409aa604c465.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/settings.rs:2311
) -> Result<T> {
let path = Settings::path()?;
let _process_guard = lock_settings_transaction(settings_transaction_mutex(&path));
with_settings_file_lock(&path, || {
operation(&SettingsTransaction { path: path.clone() })
})
}
/// Hold an exclusive advisory lock on `<settings.toml>.lock` for `operation`.
///
/// The lock file is opened (not followed) with owner-only permissions and is
/// created if absent. Dropping the `fd_lock` guard — including on an unwind —
/// releases it, and the OS releases it if the process dies, so a crash cannot
/// wedge another Codewhale instance out of its settings.
fn with_settings_file_lock<T>(path: &Path, operation: impl FnOnce() -> Result<T>) -> Result<T> {
use std::fs;
let Some(parent) = path.parent().filter(|p| !p.as_os_str().is_empty()) else {
anyhow::bail!(
"Failed to lock settings: {} has no parent directory",
path.display()
);
};
fs::create_dir_all(parent)
.with_context(|| format!("Failed to create config directory {}", parent.display()))?;
let mut lock_name = path
.file_name()
.context("Failed to lock settings: settings path has no file name")?
.to_os_string();
lock_name.push(".lock");
let lock_path = parent.join(lock_name);
reject_settings_lock_symlink(&lock_path)?;
let mut options = fs::OpenOptions::new();
options.read(true).write(true).create(true);
#[cfg(unix)]View on GitHub (pinned to 73e0f67d83)