gitbutlerapp/gitbutler · error
Could not read settings: {e:?}
Error message
Could not read settings: {e:?} What it means
The settings watcher's RwLock read failed, which in Rust means the lock is poisoned: some thread panicked while holding the write lock on the AppSettings snapshot. Every later get() then reports this wrapper error instead of settings.
Source
Thrown at crates/but-settings/src/watch.rs:96
config_dir: impl AsRef<Path>,
customization: Option<serde_json::Value>,
) -> Result<Self> {
let config_path = config_dir.as_ref().join(SETTINGS_FILE);
let app_settings = AppSettings::load(&config_path, customization.clone())?;
let app_settings = Arc::new(RwLock::new(app_settings));
Ok(Self {
config_path,
snapshot: app_settings,
customization,
})
}
/// Return a reference to the most recently loaded [`AppSettings`].
pub fn get(&self) -> Result<RwLockReadGuard<'_, AppSettings>> {
self.snapshot
.read()
.map_err(|e| anyhow::anyhow!("Could not read settings: {e:?}"))
}
/// Allow changes only from within this crate to implement all possible settings updates [here](crate::api).
pub(crate) fn get_mut_enforce_save(&self) -> Result<AppSettingsEnforceSaveToDisk<'_>> {
self.snapshot
.write()
.map(|snapshot| AppSettingsEnforceSaveToDisk {
snapshot,
config_path: &self.config_path,
saved: false,
customization: self.customization.clone(),
})
.map_err(|e| anyhow::anyhow!("Could not write settings: {e:?}"))
}
/// The path from which application settings will be read from disk.
pub fn config_path(&self) -> &Path {
&self.config_pathView on GitHub (pinned to caf1f223d3)
Solutions
- Search the log for the FIRST panic before this error — the panic is the root cause, this error is fallout
- Fix or handle the panic in the settings-mutation path (often a disk I/O unwrap or serialization panic)
- Restart the application to clear the poisoned lock after fixing or working around the root panic
- If writing new settings code, never unwrap inside get_mut_enforce_save's guard scope
Defensive patterns
Strategy: try-catch
Try / catch
match watcher.get() {
Ok(settings) => { /* use settings */ }
Err(e) if e.to_string().contains("Could not read settings") => {
// lock poisoned — restart-worthy state; fall back to defaults if acceptable
AppSettings::default()
}
Err(e) => return Err(e),
} Prevention
- Never unwrap/panic while holding the settings write lock — propagate Results instead
- Log the first panic in the process; poisoned locks are always secondary damage
- Expose a health check that reports lock poisoning so the app can restart cleanly
When it happens
Trigger: Calling AppSettingsWatcher::get() (or any read of the settings snapshot) after a panic occurred inside get_mut_enforce_save or a settings-mutation code path that held the write lock across the panic.
Common situations: A bug in a settings-save routine panics once; from then on every settings read in the process fails with this error until restart. Typically seen in logs as a cascade right after an unrelated panic message.
Related errors
- Could not write settings: {e:?}
- Failed to communicate with LM Studio server: ${error instanc
- Invalid response: ${JSON.stringify(result)}
- When using GitButler's API to summarize code, you must be lo
- When using LM Studio, you must provide a valid endpoint
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/16438cbe2546dc5a.
Report an issue: GitHub.