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

  1. Ensure shutdown() is invoked exactly once per task; guard the call site with an idempotent flag or OnceCell.
  2. Check for any code path that calls `state.tasks_completed.take()` before shutdown runs and remove it.
  3. 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

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


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)