{"record":{"id":"a6e6137eff49ba7d","repo":"gitbutlerapp/gitbutler","slug":"could-not-write-settings-e","errorCode":null,"errorMessage":"Could not write settings: {e:?}","messagePattern":"Could not write settings: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-settings/src/watch.rs","lineNumber":109,"sourceCode":"\n    /// Return a reference to the most recently loaded [`AppSettings`].\n    pub fn get(&self) -> Result<RwLockReadGuard<'_, AppSettings>> {\n        self.snapshot\n            .read()\n            .map_err(|e| anyhow::anyhow!(\"Could not read settings: {e:?}\"))\n    }\n\n    /// Allow changes only from within this crate to implement all possible settings updates [here](crate::api).\n    pub(crate) fn get_mut_enforce_save(&self) -> Result<AppSettingsEnforceSaveToDisk<'_>> {\n        self.snapshot\n            .write()\n            .map(|snapshot| AppSettingsEnforceSaveToDisk {\n                snapshot,\n                config_path: &self.config_path,\n                saved: false,\n                customization: self.customization.clone(),\n            })\n            .map_err(|e| anyhow::anyhow!(\"Could not write settings: {e:?}\"))\n    }\n\n    /// The path from which application settings will be read from disk.\n    pub fn config_path(&self) -> &Path {\n        &self.config_path\n    }\n\n    /// Start watching [`Self::config_path()`] for changes and inform\n    pub fn watch_in_background(\n        &mut self,\n        send_event: impl Fn(AppSettings) -> Result<()> + Send + Sync + 'static,\n    ) -> Result<()> {\n        let (tx, rx) = mpsc::channel();\n        let snapshot = self.snapshot.clone();\n        let config_path = self.config_path.to_owned();\n        let customization = self.customization.clone();\n        let watcher_config = Config::default()\n            .with_compare_contents(true)","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-settings/src/watch.rs#L91-L127","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before — panic under the write lock poisons it for the whole process\nlet mut guard = watcher.get_mut_enforce_save()?;\nguard.set_feature_x(true)?; // internal unwrap panics here\n// after — mutations already return Result; keep all fallible work outside unwraps\nlet mut guard = watcher.get_mut_enforce_save()?;\nguard.set_feature_x(true).context(\"persist feature_x setting\")?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = watcher.get_mut_enforce_save() {\n    if e.to_string().contains(\"Could not write settings\") {\n        // poisoned lock: surface a restart hint instead of retrying forever\n        return Err(anyhow!(\"settings lock poisoned — restart required\"));\n    }\n    return Err(e);\n}","preventionTips":["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"],"tags":["rust","rwlock","poisoned-lock","panic-recovery","but-settings"],"backgroundTag":"mutex-poisoned","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}