{"record":{"id":"35f7977c0a60b2ae","repo":"vectordotdev/vector","slug":"paired-task-no-longer-holding-wait-trigger","errorCode":null,"errorMessage":"paired task no longer holding wait trigger","messagePattern":"paired task no longer holding wait trigger","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/components/validation/sync.rs","lineNumber":145,"sourceCode":"        (trigger, handle)\n    }\n\n    /// Triggers the wait handle to wake up.\n    pub fn trigger(self) {\n        // We don't care if our trigger is actually received, because the receiver side may\n        // intentionally not be used i.e. if the code is generic in a way where only some codepaths\n        // wait to be triggered and others don't, but the trigger must always be called regardless.\n        _ = self.tx.send(());\n    }\n}\n\nimpl WaitHandle {\n    /// Waits until triggered.\n    pub async fn wait(&mut self) {\n        match self.rx.as_mut() {\n            Some(rx) => rx\n                .await\n                .expect(\"paired task no longer holding wait trigger\"),\n            None => panic!(\"tried to await wait trigger signal but has already been received\"),\n        }\n\n        // If we're here, we've successfully received the signal, so we consume the\n        // receiver, as it cannot be used/polled again.\n        self.rx.take();\n    }\n}\n\npub struct Configuring {\n    tasks_started: WaitGroup,\n    tasks_completed: WaitGroup,\n    shutdown_triggers: Mutex<Vec<WaitTrigger>>,\n}\n\npub struct Started {\n    tasks_completed: Option<WaitGroup>,\n    shutdown_triggers: Vec<WaitTrigger>,","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/src/components/validation/sync.rs#L127-L163","documentation":"WaitHandle::wait awaits a oneshot receiver whose sender ('trigger') is held by the paired coordinated task. rx.await returning Err means the sender was dropped without firing — the paired task died or was torn down while this handle was still waiting — so the wait can never be satisfied and the code panics deliberately.","triggerScenarios":"Awaiting a WaitHandle whose paired task (topology, input HTTP server, gRPC server, telemetry driver) panicked, returned early, or never got spawned, so the WaitTrigger it held was dropped; also awaiting a trigger after TaskCoordinator shutdown was already dropped.","commonSituations":"A component topology crashes during validation so the 'started'/'completed' trigger never fires; a test forgets to spawn the task that holds the trigger; teardown races in run_validation where the coordinator drops triggers before wait() is polled.","solutions":["Find why the paired task died: check its JoinHandle/panic output and crash_rx messages from the topology.","Ensure the task holding the WaitTrigger is spawned before calling wait() and outlives the wait.","Check WaitHandle isn't awaited after the TaskCoordinator transitioned past its phase and dropped triggers."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match rx.await {\n    Ok(()) => {},\n    Err(_) => panic!(\"paired task dropped its wait trigger without firing\"),\n}","preventionTips":["Spawn the task that holds the WaitTrigger before awaiting the handle.","Check the paired task's JoinHandle/panic output when wait never resolves.","Don't drop the TaskCoordinator while handles are still waiting."],"tags":["sync","oneshot","shutdown"],"backgroundTag":"internal-invariant-violation","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"}