vectordotdev/vector · error

Every ContainerLogInfo has it's ContainerState

Error message

Every ContainerLogInfo has it's ContainerState

What it means

docker_logs runs an internal actor that owns containers: HashMap<ContainerId, ContainerState>. When a container's log-stream future finishes it returns its ContainerLogInfo to the actor, which resolves the entry with containers.get_mut(&info.id).expect("Every ContainerLogInfo has it's ContainerState"). The expect encodes the bookkeeping invariant that every started id has a live entry; it panics when an info message arrives for an id that is not (or no longer) in the map, i.e. the start/finish bookkeeping desynchronized inside Vector.

Source

Thrown at src/sources/docker_logs/mod.rs:597

                }

                let id = ContainerId::new(id);
                self.containers.insert(id.clone(), self.esb.start(id, None));
            });

        Ok(self)
    }

    async fn run(mut self) {
        loop {
            tokio::select! {
                value = self.main_recv.recv() => {
                    match value {
                        Some(Ok(info)) => {
                            let state = self
                                .containers
                                .get_mut(&info.id)
                                .expect("Every ContainerLogInfo has it's ContainerState");
                            if state.return_info(info) {
                                self.esb.restart(state);
                            }
                        },
                        Some(Err((id,persistence))) => {
                            let state = self
                                .containers
                                .remove(&id)
                                .expect("Every started ContainerId has it's ContainerState");
                            match persistence{
                                ErrorPersistence::Transient => if state.is_running() {
                                    let backoff= Some(self.backoff_duration);
                                    self.containers.insert(id.clone(), self.esb.start(id, backoff));
                                }
                                // Forget the container since the error is permanent.
                                ErrorPersistence::Permanent => (),
                            }
                        }

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Capture a repro (Vector version, docker events around the panic, debug logs for docker_logs) and file an upstream issue - this is an internal invariant break
  2. Move to a Vector release where docker_logs is known stable for your churn pattern
  3. Patch locally: replace the expect with a match that warns and skips the stale info
  4. Run the source under a supervisor/restart policy until patched

Example fix

// before
let state = self
    .containers
    .get_mut(&info.id)
    .expect("Every ContainerLogInfo has it's ContainerState");

// after
let Some(state) = self.containers.get_mut(&info.id) else {
    warn!(message = "ContainerLogInfo without ContainerState, skipping", id = %info.id);
    continue;
};
Defensive patterns

Strategy: validation

Validate before calling

// before accessing the map in the actor loop:
match self.containers.get_mut(&info.id) {
    Some(state) => { /* return_info / restart logic */ }
    None => {
        warn!(message = "stale ContainerLogInfo", id = %info.id);
        continue;
    }
}

Type guard

fn state_of<'a>(
    containers: &'a mut HashMap<ContainerId, ContainerState>,
    id: &ContainerId,
) -> Option<&'a mut ContainerState> {
    containers.get_mut(id)
}

Try / catch

// Rust panics in async tasks are not catchable per-message; guard instead:
if let Some(state) = self.containers.get_mut(&info.id) {
    if state.return_info(info) {
        self.esb.restart(state);
    }
} else {
    warn!(id = %info.id, "container info without state");
}

Prevention

When it happens

Trigger: A ContainerLogInfo delivered after the error path already removed the state for that id, or a future returning its info twice - both are internal ordering bugs between esb.start/restart, return_info, and the map mutations, not a Docker-side condition.

Common situations: Docker engine restarts and rapid container churn stressing the restart bookkeeping, or regressions after docker_logs refactors in specific Vector versions.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/ac2c6d3cf7baa338. Report an issue: GitHub.