{"record":{"id":"e794a48a038f59dc","repo":"vectordotdev/vector","slug":"poisoned-locks-are-dumb","errorCode":null,"errorMessage":"poisoned locks are dumb","messagePattern":"poisoned locks are dumb","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/trace.rs","lineNumber":217,"sourceCode":"}\n\n/// Attempts to get the trace sender for sending internal log events.\n///\n/// If the trace sender has not yet been created, `None` is returned.\nfn maybe_get_trace_sender() -> Option<&'static broadcast::Sender<LogEvent>> {\n    SENDER.get()\n}\n\n/// Creates a trace receiver that receives internal log events.\n///\n/// This will create a trace sender if one did not already exist.\nfn get_trace_receiver() -> broadcast::Receiver<LogEvent> {\n    get_trace_sender().subscribe()\n}\n\n/// Gets a mutable reference to the list of waiting subscribers, if it exists.\nfn get_trace_subscriber_list() -> MutexGuard<'static, Option<Vec<oneshot::Sender<Vec<LogEvent>>>>> {\n    SUBSCRIBERS.lock().expect(\"poisoned locks are dumb\")\n}\n\n/// Attempts to register for early buffered events.\n///\n/// If early buffering has not yet been stopped, `Some(receiver)` is returned. The given receiver will resolve to a\n/// vector of all early buffered events once early buffering has been stopped. Otherwise, if early buffering is already\n/// stopped, `None` is returned.\nfn try_register_for_early_events() -> Option<oneshot::Receiver<Vec<LogEvent>>> {\n    if SHOULD_BUFFER.load(Ordering::Acquire) {\n        // We're still in early buffering mode. Attempt to subscribe by adding a oneshot sender\n        // to SUBSCRIBERS. If it's already been consumed, then we've gotten beaten out by a\n        // caller that is disabling early buffering, so we just go with the flow either way.\n        get_trace_subscriber_list().as_mut().map(|subscribers| {\n            let (tx, rx) = oneshot::channel();\n            subscribers.push(tx);\n            rx\n        })\n    } else {","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/src/trace.rs#L199-L235","documentation":"`get_trace_subscriber_list` locks the static `SUBSCRIBERS` mutex holding waiting oneshot senders for early trace events. A poisoned lock (prior panic while holding it) makes `.lock()` return `Err`, and the `expect(\"poisoned locks are dumb\")` panics. Like the BUFFER panic, it indicates corrupted internal tracing state.","triggerScenarios":"Any call to `get_trace_subscriber_list` (via `try_register_for_early_events` or `stop_early_buffering`) after a thread panicked while holding the SUBSCRIBERS lock.","commonSituations":"Cascading failure after an earlier panic in the trace-subscriber registration path; typically seen in tests or shutdown code where one thread panicked while registering subscribers.","solutions":["Locate and fix the original panic that poisoned SUBSCRIBERS (appears earlier in logs)","Restart the process — poisoned mutexes are unrecoverable","Keep panics out of code that runs while the subscriber list is locked","Replace `.expect` with `unwrap_or_else(|p| p.into_inner())` if a fork tolerates recovery"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// catch panics around subscriber registration paths\nlet result = std::panic::catch_unwind(try_register_for_early_events);\nif result.is_err() { eprintln!(\"trace subscriber state poisoned\"); }","preventionTips":["Fix the originating panic that poisons SUBSCRIBERS","Keep subscriber-list mutations panic-free","Restart on poisoning; it is unrecoverable","In forks, prefer `into_inner()` recovery over expect"],"tags":["rust","panic","mutex-poisoning","tracing"],"backgroundTag":"invalid-state-transition","analyzedSha":"bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013","analyzedAt":"2026-09-16T02:53:35.741Z","contentChangedAt":"2026-09-16T02:53:35.741Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}