{"record":{"id":"7ae04b3a8adfca81","repo":"risingwavelabs/risingwave","slug":"should-exist-7ae04b","errorCode":null,"errorMessage":"should exist","messagePattern":"should exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/meta/src/manager/sink_coordination/coordinator_worker.rs","lineNumber":431,"sourceCode":"                        CoordinationHandleManagerEvent::Stop\n                    }\n                    coordinate_request::Msg::StartRequest(_) => {\n                        unreachable!(\"should have been handled\");\n                    }\n                };\n                Ok((handle_id, event))\n            }\n        }\n    }\n\n    fn vnode_bitmap(&self, handle_id: HandleId) -> &Bitmap {\n        self.writer_handles[&handle_id].vnode_bitmap()\n    }\n\n    fn stop_handle(&mut self, handle_id: HandleId) -> anyhow::Result<()> {\n        self.writer_handles\n            .remove(&handle_id)\n            .expect(\"should exist\")\n            .stop()\n    }\n\n    async fn wait_init_handles(&mut self) -> anyhow::Result<HashSet<HandleId>> {\n        assert!(self.writer_handles.is_empty());\n        let mut init_requests = AligningRequests::default();\n        while !init_requests.aligned() {\n            let (handle_id, event) = self.next_event().await?;\n            let unexpected_event = match event {\n                CoordinationHandleManagerEvent::NewHandle => {\n                    init_requests.add_new_request(handle_id, (), self.vnode_bitmap(handle_id))?;\n                    continue;\n                }\n                event => event.name(),\n            };\n            return Err(anyhow!(\n                \"expect new handle during init, but got {}\",\n                unexpected_event","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/manager/sink_coordination/coordinator_worker.rs#L413-L449","documentation":"This is a panic from `.expect(\"should exist\")` in `stop_handle`, thrown when the writer handle being stopped is no longer present in the `writer_handles` map. The coordinator assumes a handle id referenced during the alter-parallelism protocol is always registered, so a missing entry is an internal invariant violation rather than a recoverable error. The process/thread panics instead of returning an error.","triggerScenarios":"Calling `alter_parallelisms` on a handle_id that was never registered via `NewHandle`, or calling `stop_handle` twice on the same HandleId (the first `remove` already deleted it). Any code path that removes handles outside this worker also breaks the invariant.","commonSituations":"Bugs in the sink coordination protocol state machine where a handle was stopped twice (e.g. a Stop event processed twice), a stale HandleId from a previous coordinator round being reused, or a refactored caller that forgets to add new handles before altering parallelisms.","solutions":["Ensure each HandleId is only stopped once: remove it from `remaining_handles` (already asserted) and never call `alter_parallelisms` for an id that already went through Stop.","Verify the handle was added via `CoordinationHandleManagerEvent::NewHandle` before calling `alter_parallelisms`; check upstream registration logic.","If a benign double-stop is possible, replace `.expect` with `ok_or_else(|| anyhow!(...))` and return an error so the failure is diagnosable instead of a panic.","Capture logs of the handle lifecycle (NewHandle/Stop events) to find which handle_id double-fires."],"exampleFix":"// before\nself.writer_handles\n    .remove(&handle_id)\n    .expect(\"should exist\")\n    .stop()\n// after\nself.writer_handles\n    .remove(&handle_id)\n    .ok_or_else(|| anyhow!(\"handle {:?} does not exist when stopping\", handle_id))?\n    .stop()","handlingStrategy":"validation","validationCode":"// coordinator-side precheck\nif !self.writer_handles.contains_key(&handle_id) {\n    return Err(anyhow!(\"cannot stop unregistered handle {:?}\", handle_id));\n}","typeGuard":"fn get_handle(handles: &HashMap<HandleId, WriterHandle>, id: &HandleId) -> Option<&WriterHandle> { handles.get(id) }","tryCatchPattern":"// avoid .expect/.unwrap on map lookups; use ok_or_else\nlet h = handles.remove(&id).ok_or_else(|| anyhow!(\"missing handle\"))?;","preventionTips":["Never call stop_handle for the same HandleId twice; track stopped ids.","Assert at call sites that handles were registered via NewHandle before altering parallelisms.","Prefer anyhow errors over .expect for recoverable protocol states."],"tags":["rust","panic","invariant","sink-coordination"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}