{"record":{"id":"6fd1f34506170ad3","repo":"quickwit-oss/quickwit","slug":"lock-should-not-be-poisoned","errorCode":null,"errorMessage":"lock should not be poisoned","messagePattern":"lock should not be poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-common/src/pubsub.rs","lineNumber":81,"sourceCode":"#[derive(Debug, Default)]\nstruct InnerEventBroker {\n    subscription_sequence: AtomicUsize,\n    subscriptions: Mutex<TypeMap>,\n}\n\nimpl EventBroker {\n    // The point of this private method is to allow the public subscribe method to have only one\n    // generic argument and avoid the ugly `::<E, _>` syntax.\n    fn subscribe_aux<E, S>(&self, subscriber: S, with_timeout: bool) -> EventSubscriptionHandle\n    where\n        E: Event,\n        S: EventSubscriber<E> + Send + Sync + 'static,\n    {\n        let mut subscriptions = self\n            .inner\n            .subscriptions\n            .lock()\n            .expect(\"lock should not be poisoned\");\n\n        if !subscriptions.contains::<EventSubscriptions<E>>() {\n            subscriptions.insert::<EventSubscriptions<E>>(HashMap::new());\n        }\n        let subscription_id = self\n            .inner\n            .subscription_sequence\n            .fetch_add(1, Ordering::Relaxed);\n\n        let subscriber_name = std::any::type_name::<S>();\n        let subscription = EventSubscription {\n            subscriber_name,\n            subscriber: Arc::new(TokioMutex::new(Box::new(subscriber))),\n            with_timeout,\n        };\n        let typed_subscriptions = subscriptions\n            .get_mut::<EventSubscriptions<E>>()\n            .expect(\"subscription map should exist\");","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-common/src/pubsub.rs#L63-L99","documentation":"This panic fires when the Mutex guarding the event broker's subscription map is poisoned, i.e. a previous thread panicked while holding the lock. The library treats lock poisoning as an unrecoverable internal invariant violation and aborts with 'lock should not be poisoned' instead of propagating the poison.","triggerScenarios":"Calling EventBroker::subscribe or subscribe_without_timeout after another thread panicked while holding the broker's subscriptions lock (e.g. a subscriber's Drop impl or a publish-triggered callback panicked inside the critical section).","commonSituations":"A panicking event handler or a buggy EventSubscriber::Drop implementation poisons the lock; subsequent subscribe calls on the shared broker then panic. Often seen in long-lived brokers shared across many test threads.","solutions":["Find and fix the original panic that occurred while the subscriptions lock was held (it is the root cause, this panic is secondary)","Keep subscriber callbacks and Drop implementations panic-free, or catch_unwind around user callbacks","Use a lock type that tolerates poisoning (parking_lot::Mutex has no poisoning) if recovery is desired","Recreate the EventBroker instead of reusing a broker whose lock was poisoned"],"exampleFix":"// before: user callback can panic inside trigger while lock held\nsubscription.trigger(event.clone());\n// after: isolate user code\nlet _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| subscription.trigger(event.clone())));","handlingStrategy":"try-catch","validationCode":"// No pre-call validation possible; detect poison via try_lock\nif broker.inner.subscriptions.try_lock().is_err() { eprintln!(\"broker lock contended/poisoned — do not reuse\"); }","typeGuard":"fn broker_healthy<T>(lock: &std::sync::Mutex<T>) -> bool { lock.try_lock().is_ok() }","tryCatchPattern":"let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| broker.subscribe(subscriber)));\nmatch result { Ok(h) => h, Err(_) => { /* rebuild broker */ } }","preventionTips":["Never let user callbacks or Drop impls panic while holding the broker lock","Use catch_unwind around subscriber trigger calls","Prefer parking_lot::Mutex (non-poisoning) for broker internals","One panicking subscriber should not be allowed to poison a shared broker"],"tags":["rust","concurrency","mutex-poisoned","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}