{"record":{"id":"1c7f8e9e9745093b","repo":"vectordotdev/vector","slug":"couldn-t-broadcast-config-changes","errorCode":null,"errorMessage":"Couldn't broadcast config changes.","messagePattern":"Couldn't broadcast config changes\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/topology/running.rs","lineNumber":936,"sourceCode":"                                .map(|key| key.to_string()),\n                        )\n                        .collect(),\n                    sink_keys: diff\n                        .sinks\n                        .changed_and_added()\n                        .map(|key| key.to_string())\n                        .chain(added_changed_tables.iter().map(|key| key.to_string()))\n                        .collect(),\n                    // Note, only sources and transforms are relevant. Sinks do\n                    // not have outputs to tap.\n                    removals,\n                    type_names: self\n                        .component_type_names\n                        .iter()\n                        .map(|(k, v)| (k.to_string(), v.clone()))\n                        .collect(),\n                })\n                .expect(\"Couldn't broadcast config changes.\");\n        }\n    }\n\n    fn setup_outputs(&mut self, key: &ComponentKey, new_pieces: &mut builder::TopologyPieces) {\n        let outputs = new_pieces.outputs.remove(key).unwrap();\n        for (port, output) in outputs {\n            debug!(component_id = %key, output_id = ?port, \"Configuring output for component.\");\n\n            let id = OutputId {\n                component: key.clone(),\n                port,\n            };\n\n            self.outputs.insert(id, output);\n        }\n    }\n\n    fn setup_inputs(","sourceCodeStart":918,"sourceCodeEnd":954,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/src/topology/running.rs#L918-L954","documentation":"`connect_diff` broadcasts a config-change diff over a broadcast channel and panics via `.expect(\"Couldn't broadcast config changes.\")` if the send fails (src/topology/running.rs:936). With tokio broadcast channels, send only fails when there are no live receivers. Since topology reload expects its control-plane receivers to be alive, failure means the watching tasks have all exited.","triggerScenarios":"Calling `reload_config_and_respawn` -> `connect_diff` after the diff-broadcast receiver tasks (e.g. handle-based watchers) have been dropped or shut down.","commonSituations":"A topology reload issued after the API/handle subscribers died; application shutting down while a reload races through connect_diff.","solutions":["Ensure the topology's control receivers (handle watchers) outlive any reload attempt before calling reload.","Serialize reload against shutdown so connect_diff does not run during teardown.","Recreate the running topology instead of reloading if its watchers are known dead."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if watchers_alive(&topology.handles) { topology.connect_diff(diff).await; }","typeGuard":null,"tryCatchPattern":"// tokio broadcast send failure = no receivers; detect via receiver count before reload\nif topology.handles.receiver_count() == 0 { log::error!(\"watchers gone; cannot reload\"); return; }","preventionTips":["Hold the control-channel receivers for the lifetime of the running topology.","Serialize reloads against shutdown with a shutdown token/flag.","Prefer full restart over reload if watcher health is uncertain."],"tags":["rust","topology","reload","broadcast-channel"],"backgroundTag":"invalid-state-transition","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"}