vectordotdev/vector · error
poisoned
Error message
poisoned
What it means
register_for_shutdown locks the TaskCoordinator's shared Mutex of shutdown triggers and asserts the lock isn't poisoned. The lock is only poisoned if another thread panicked while holding it (e.g. during WaitTrigger::new or Vec push). This panic indicates a prior panic somewhere in coordinator-managed code, surfaced here as a lock error.
Solutions
- Look for the ORIGINAL panic earlier in the test output — the poisoning panic is the root cause, not this one.
- Replace .expect("poisoned") with .unwrap_or_else(PoisonError::into_inner) during debugging to proceed and expose the state.
- Avoid panicking while holding the coordinator's mutexes (keep lock scopes minimal, clone data out before fallible work).
Example fix
// before
.lock().expect("poisoned").push(trigger);
// after
.lock().unwrap_or_else(PoisonError::into_inner).push(trigger); Defensive patterns
Strategy: try-catch
Try / catch
match self.state.shutdown_triggers.lock() {
Ok(mut triggers) => triggers.push(trigger),
Err(e) => panic!("coordinator mutex poisoned by earlier panic: {e}"),
} Prevention
- Always hunt for the FIRST panic in the output — 'poisoned' is always secondary.
- Keep lock scopes minimal; never run fallible/panicking code while holding coordinator locks.
- Use PoisonError::into_inner recovery temporarily while debugging cascades.
When it happens
Trigger: Calling register_for_shutdown (from spawn_input_http_server, spawn_grpc_server, spawn_component_topology, or into_collector) after some thread panicked while holding the shutdown_triggers mutex during coordinator setup/teardown.
Common situations: An earlier panic inside the validation framework's setup (e.g. topology construction) leaving the mutex poisoned; cascading panics in tests where the first failure is masked by this 'poisoned' panic.
Related errors
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/fa44d8a2a3bfe383.
Report an issue: GitHub.
Appendix: source
Thrown at src/components/validation/sync.rs:217
impl TaskCoordinator<Configuring> {
/// Attaches a new child to the wait group that tracks when tasks have started.
pub fn track_started(&self) -> WaitGroupChild {
self.state.tasks_started.add_child()
}
/// Attaches a new child to the wait group that tracks when tasks have completed.
pub fn track_completed(&self) -> WaitGroupChild {
self.state.tasks_completed.add_child()
}
/// Registers a handle that will be notified when shutdown is triggered.
pub fn register_for_shutdown(&self) -> WaitHandle {
let (trigger, handle) = WaitTrigger::new();
self.state
.shutdown_triggers
.lock()
.expect("poisoned")
.push(trigger);
handle
}
/// Waits for all tasks to have marked that they have started.
pub async fn started(self) -> TaskCoordinator<Started> {
let Configuring {
mut tasks_started,
tasks_completed,
shutdown_triggers,
} = self.state;
tasks_started.wait_for_children().await;
trace!("All coordinated tasks reported as having started.");
TaskCoordinator {
state: Started {
tasks_completed: Some(tasks_completed),View on GitHub (pinned to bdb87aeaa4)