vectordotdev/vector · error
tasks completed wait group already consumed
Error message
tasks completed wait group already consumed
What it means
During validation shutdown, the component asserts that its `tasks_completed` wait group is still present (`Some`). It is a `take()`-style option: once shutdown runs a second time the wait group has been consumed, and `expect` panics with "tasks completed wait group already consumed". This is an internal invariant guard against double-shutdown of a validated task.
Solutions
- Ensure shutdown() is invoked exactly once per task; guard the call site with an idempotent flag or OnceCell.
- Check for any code path that calls `state.tasks_completed.take()` before shutdown runs and remove it.
- If a double shutdown is legitimately possible in your driver, recreate the task instead of reusing it.
Example fix
// before task.shutdown().await; task.shutdown().await; // panics: wait group already consumed // after task.shutdown().await; // do not call shutdown twice; drop the task instead
Defensive patterns
Strategy: try-catch
Validate before calling
if task.is_shutdown() { return; } // or track a `shutdown_done: bool` before calling Type guard
fn can_shutdown(task: &TaskHandle) -> bool { !task.shutdown_started() } Try / catch
// Rust panics are not catchable by callers of the API; instead make the call idempotent:
let mut did_shutdown = false;
if !did_shutdown { handle.shutdown().await; did_shutdown = true; } Prevention
- Treat shutdown as a one-shot operation; wrap it in a guard type that prevents double invocation.
- Never manually consume state fields (`take()`) that shutdown owns.
- In tests, drop task handles after shutdown instead of reusing them.
When it happens
Trigger: Calling `shutdown()` twice on the same validated component/task in src/components/validation/sync.rs, or otherwise consuming `state.tasks_completed` (via `take()`) before `shutdown()` reads it.
Common situations: Bugs in validation harness code that run the shutdown path twice, component re-registration where the old task object is shut down again, or custom test drivers that tear down tasks manually and then call the public shutdown API.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- can't be empty
- Drain deadline received after completion.
- errors ignored
- event forward rx should not close first
- file server exited with an error
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/4f76c25264f51978.
Report an issue: GitHub.
Appendix: source
Thrown at src/components/validation/sync.rs:263
pub async fn shutdown(&mut self) {
info!("{}: triggering task to shutdown.", self.name);
// Trigger all registered shutdown handles.
for trigger in self.state.shutdown_triggers.drain(..) {
trigger.trigger();
debug!("{}: shutdown triggered for coordinated tasks.", self.name);
}
// Now simply wait for all of them to mark themselves as completed.
debug!(
"{}: waiting for coordinated tasks to complete...",
self.name
);
let tasks_completed = self
.state
.tasks_completed
.as_mut()
.expect("tasks completed wait group already consumed");
tasks_completed.wait_for_children().await;
info!("{}: task has been shutdown.", self.name);
}
}
View on GitHub (pinned to bdb87aeaa4)