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

  1. Locate the originating panic in logs (it precedes all 'Could not write settings: PoisonError' entries)
  2. Fix the panicking mutation path — common culprits are unwraps on file I/O or serialization inside the write guard
  3. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/a6e6137eff49ba7d. Report an issue: GitHub.