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

  1. Look for the ORIGINAL panic earlier in the test output — the poisoning panic is the root cause, not this one.
  2. Replace .expect("poisoned") with .unwrap_or_else(PoisonError::into_inner) during debugging to proceed and expose the state.
  3. 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

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)