gitbutlerapp/gitbutler · error · anyhow::Error
Could not write settings: {e:?}
Error message
Could not write settings: {e:?} What it means
The settings watcher's RwLock write acquisition failed because the lock is poisoned — a previous thread panicked while holding it during a settings update. Any attempt to take the write guard (get_mut_enforce_save, used by all in-crate settings mutations) now returns this error.
Source
Thrown at crates/but-settings/src/watch.rs:109
/// 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_path
}
/// Start watching [`Self::config_path()`] for changes and inform
pub fn watch_in_background(
&mut self,
send_event: impl Fn(AppSettings) -> Result<()> + Send + Sync + 'static,
) -> Result<()> {
let (tx, rx) = mpsc::channel();
let snapshot = self.snapshot.clone();
let config_path = self.config_path.to_owned();
let customization = self.customization.clone();
let watcher_config = Config::default()
.with_compare_contents(true)View on GitHub (pinned to caf1f223d3)
Solutions
- Locate the originating panic in logs (it precedes all 'Could not write settings: PoisonError' entries)
- Fix the panicking mutation path — common culprits are unwraps on file I/O or serialization inside the write guard
- Restart the process to recover the settings lock once the root cause is addressed
Example fix
// before — panic under the write lock poisons it for the whole process
let mut guard = watcher.get_mut_enforce_save()?;
guard.set_feature_x(true)?; // internal unwrap panics here
// after — mutations already return Result; keep all fallible work outside unwraps
let mut guard = watcher.get_mut_enforce_save()?;
guard.set_feature_x(true).context("persist feature_x setting")?; Defensive patterns
Strategy: try-catch
Try / catch
if let Err(e) = watcher.get_mut_enforce_save() {
if e.to_string().contains("Could not write settings") {
// poisoned lock: surface a restart hint instead of retrying forever
return Err(anyhow!("settings lock poisoned — restart required"));
}
return Err(e);
} Prevention
- Keep the write-guard scope minimal; do fallible I/O outside the guard where possible
- Convert every unwrap inside settings mutation into `?` before shipping
- Alert on the first poison event rather than the read/write errors that follow
When it happens
Trigger: Calling a settings-update API (anything routed through get_mut_enforce_save) after an earlier panic inside a settings mutation that held the write lock.
Common situations: Settings save code panics (e.g. unwrap on a failing write, panic in customization merge); subsequently every settings change in that process fails with 'Could not write settings: PoisonError'. Users see settings toggles silently stop persisting.
Related errors
- Could not read 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/a6e6137eff49ba7d.
Report an issue: GitHub.