{"record":{"id":"c137ba5c9e3af493","repo":"vectordotdev/vector","slug":"mutex-poisoned","errorCode":null,"errorMessage":"mutex poisoned","messagePattern":"mutex poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/utilization.rs","lineNumber":229,"sourceCode":"/// Registry for components sending utilization data.\n///\n/// Cloning this is cheap and does not clone the underlying data.\n#[derive(Clone)]\npub struct UtilizationRegistry {\n    timers: Arc<Mutex<HashMap<ComponentKey, Timer>>>,\n    timer_tx: Sender<UtilizationTimerMessage>,\n}\n\nimpl UtilizationRegistry {\n    /// Adds a new component to this utilization metric emitter\n    ///\n    /// Returns a sender which can be used to send utilization information back to the emitter\n    pub(crate) fn add_component(\n        &self,\n        key: ComponentKey,\n        gauge: Gauge,\n    ) -> UtilizationComponentSender {\n        self.timers.lock().expect(\"mutex poisoned\").insert(\n            key.clone(),\n            Timer::new(\n                gauge,\n                #[cfg(debug_assertions)]\n                key.id().into(),\n            ),\n        );\n        UtilizationComponentSender {\n            timer_tx: self.timer_tx.clone(),\n            component_key: key,\n        }\n    }\n\n    /// Removes a component from this utilization metric emitter\n    pub(crate) fn remove_component(&self, key: &ComponentKey) {\n        self.timers.lock().expect(\"mutex poisoned\").remove(key);\n    }\n}","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/vectordotdev/vector/blob/3708c39b12a93212ed8b8d7510b4cc7769cb5864/src/utilization.rs#L211-L247","documentation":"The utilization (busy/idle ratio) subsystem keeps per-component timers in a `std::sync::Mutex<HashMap<ComponentKey, Timer>>`. `add_component` locks it with `.expect(\"mutex poisoned\")`; a panic anywhere inside a critical section on this mutex (insertion, removal, message handling) poisons it, after which every later `add_component` — each new component at startup or config reload — panics with the same message.","triggerScenarios":"A prior panic while the timers lock was held (during an earlier add_component/remove_component or a timer update in run_utilization); the next component registration then hits the poisoned lock.","commonSituations":"Appears cascading after an earlier panic during topology build or reload; the utilization metrics emitter then fails repeatedly for every new component, masking the original fault.","solutions":["Locate the first panic before the \"mutex poisoned\" messages in the logs and fix/upgrade for it","Restart Vector to clear the poisoned mutex","If embedding: recover with `lock().unwrap_or_else(|e| e.into_inner())` — the map is still usable for metrics — or use parking_lot::Mutex which cannot poison"],"exampleFix":"// before\nself.timers.lock().expect(\"mutex poisoned\").insert(key.clone(), Timer::new(gauge));\n// after\nself.timers\n    .lock()\n    .unwrap_or_else(|poisoned| poisoned.into_inner())\n    .insert(key.clone(), Timer::new(gauge));","handlingStrategy":"fallback","validationCode":"# shell: utilization poisoning always follows an earlier panic\njournalctl -u vector | grep -m1 -B5 'panicked'","typeGuard":null,"tryCatchPattern":"// Rust (embedding): best-effort metrics should not poison-shutdown the registry\nlet timers = self\n    .timers\n    .lock()\n    .unwrap_or_else(|poisoned| poisoned.into_inner());","preventionTips":["Alert on the first panic, not the cascade of \"mutex poisoned\" messages","Restart to clear poisoned locks after fixing the original fault","For metrics-only shared state, prefer parking_lot or recovered locks over expect"],"tags":["mutex","poisoning","utilization","metrics","panic"],"backgroundTag":"mutex-poisoned","analyzedSha":"3708c39b12a93212ed8b8d7510b4cc7769cb5864","analyzedAt":"2026-08-20T07:02:18.786Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}