{"record":{"id":"c65a6d965df20c04","repo":"vectordotdev/vector","slug":"every-started-containerid-has-it-s-containerstate","errorCode":null,"errorMessage":"Every started ContainerId has it's ContainerState","messagePattern":"Every started ContainerId has it's ContainerState","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/sources/docker_logs/mod.rs","lineNumber":606,"sourceCode":"    async fn run(mut self) {\n        loop {\n            tokio::select! {\n                value = self.main_recv.recv() => {\n                    match value {\n                        Some(Ok(info)) => {\n                            let state = self\n                                .containers\n                                .get_mut(&info.id)\n                                .expect(\"Every ContainerLogInfo has it's ContainerState\");\n                            if state.return_info(info) {\n                                self.esb.restart(state);\n                            }\n                        },\n                        Some(Err((id,persistence))) => {\n                            let state = self\n                                .containers\n                                .remove(&id)\n                                .expect(\"Every started ContainerId has it's ContainerState\");\n                            match persistence{\n                                ErrorPersistence::Transient => if state.is_running() {\n                                    let backoff= Some(self.backoff_duration);\n                                    self.containers.insert(id.clone(), self.esb.start(id, backoff));\n                                }\n                                // Forget the container since the error is permanent.\n                                ErrorPersistence::Permanent => (),\n                            }\n                        }\n                        None => {\n                            error!(message = \"The docker_logs source main stream has ended unexpectedly.\", internal_log_rate_limit = false);\n                            info!(message = \"Shutting down docker_logs source.\");\n                            return;\n                        }\n                    };\n                }\n                value = self.events.next() => {\n                    match value {","sourceCodeStart":588,"sourceCodeEnd":624,"githubUrl":"https://github.com/vectordotdev/vector/blob/3708c39b12a93212ed8b8d7510b4cc7769cb5864/src/sources/docker_logs/mod.rs#L588-L624","documentation":"In the docker_logs actor's error branch, a failed log stream reports (id, ErrorPersistence); the actor removes its state with containers.remove(&id).expect(\"Every started ContainerId has it's ContainerState\"). The invariant: an error can only arrive for a container the actor started and still tracks. The panic fires when an error arrives for an id that was already removed or never inserted - the same bookkeeping desync as the info path, seen from the failure side.","triggerScenarios":"An error message for a container whose state was already removed (double-report of a failed stream, or a race between return_info and the error report for the same id).","commonSituations":"Docker daemon restart storms, aggressive container churn, or docker_logs refactors/regressions in a given Vector version.","solutions":["Gather logs (Vector version, docker events, debug output) and report upstream as an internal invariant break","Upgrade/downgrade to a docker_logs-stable release","Patch: use containers.remove(&id) and warn-and-continue on None instead of expecting","Supervise and restart Vector on this panic until a fixed build is deployed"],"exampleFix":"// before\nlet state = self\n    .containers\n    .remove(&id)\n    .expect(\"Every started ContainerId has it's ContainerState\");\n\n// after\nlet Some(state) = self.containers.remove(&id) else {\n    warn!(message = \"error reported for unknown container, skipping\", id = %id);\n    continue;\n};","handlingStrategy":"validation","validationCode":"// before removing in the error branch:\nif let Some(state) = self.containers.remove(&id) {\n    // existing ErrorPersistence handling\n} else {\n    warn!(message = \"error for untracked container\", id = %id);\n    continue;\n}","typeGuard":"fn remove_state(\n    containers: &mut HashMap<ContainerId, ContainerState>,\n    id: &ContainerId,\n) -> Option<ContainerState> {\n    containers.remove(id)\n}","tryCatchPattern":"match self.containers.remove(&id) {\n    Some(state) => match persistence {\n        ErrorPersistence::Transient => { /* restart */ }\n        ErrorPersistence::Permanent => (),\n    },\n    None => warn!(id = %id, \"duplicate or unknown container error\"),\n}","preventionTips":["Treat duplicate error reports as expected, not impossible: guard the remove","Keep start/finish bookkeeping single-sourced so an id cannot be finished twice","Stress-test with docker engine restarts to surface ordering races"],"tags":["rust","panic","invariant","docker-logs","hashmap","actor"],"backgroundTag":"invariant-assertion-failed","analyzedSha":"3708c39b12a93212ed8b8d7510b4cc7769cb5864","analyzedAt":"2026-08-20T07:02:18.786Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}