risingwavelabs/risingwave · error
should have been handled
Error message
should have been handled
What it means
`next_event` matches on the received `coordinate_request::Msg` and maps each variant to an event; `StartRequest` should never appear at this point because start requests are handled earlier in `next_event` (when the handle is first registered). Hitting `unreachable!("should have been handled")` means a `StartRequest` was dispatched through the inner request path anyway — a broken invariant in the request routing logic.
Source
Thrown at src/meta/src/manager/sink_coordination/coordinator_worker.rs:416
let event = match request {
coordinate_request::Msg::CommitRequest(request) => {
CoordinationHandleManagerEvent::CommitRequest {
epoch: request.epoch,
metadata: request.metadata.ok_or_else(|| anyhow!("empty sink metadata"))?,
schema_change: request.schema_change,
}
}
coordinate_request::Msg::AlignInitialEpochRequest(epoch) => {
CoordinationHandleManagerEvent::AlignInitialEpoch(epoch)
}
coordinate_request::Msg::UpdateVnodeRequest(_) => {
CoordinationHandleManagerEvent::UpdateVnodeBitmap
}
coordinate_request::Msg::Stop(_) => {
CoordinationHandleManagerEvent::Stop
}
coordinate_request::Msg::StartRequest(_) => {
unreachable!("should have been handled");
}
};
Ok((handle_id, event))
}
}
}
fn vnode_bitmap(&self, handle_id: HandleId) -> &Bitmap {
self.writer_handles[&handle_id].vnode_bitmap()
}
fn stop_handle(&mut self, handle_id: HandleId) -> anyhow::Result<()> {
self.writer_handles
.remove(&handle_id)
.expect("should exist")
.stop()
}
View on GitHub (pinned to 6469eb736d)
Solutions
- Inspect the writer to ensure it sends `StartRequest` at most once per handle; add idempotency on the writer's retry path.
- Handle `StartRequest` defensively in the `match` (e.g. log and skip) instead of panicking, if duplicates are plausible.
- Review recent changes to `next_event`/`next_request_inner` that may have broken start-request routing.
- Check meta-node logs for the sink id and handle id to identify which writer re-sent the request.
Example fix
// before
coordinate_request::Msg::StartRequest(_) => {
unreachable!("should have been handled");
}
// after
coordinate_request::Msg::StartRequest(_) => {
warn!(handle_id, "duplicate StartRequest in request stream; ignoring");
continue;
} Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure the writer sends StartRequest only once per handle
let mut start_sent = false;
fn send_start(tx: &Sender<StartRequest>, start_sent: &mut bool) -> Result<()> {
ensure!(!*start_sent, "StartRequest already sent");
*start_sent = true;
tx.send(start_request)
} Type guard
fn is_registration_path(msg: &Msg) -> bool {
!matches!(msg, coordinate_request::Msg::StartRequest(_))
} Try / catch
coordinate_request::Msg::StartRequest(_) => {
warn!(handle_id, "unexpected StartRequest in request stream; ignoring instead of panicking");
continue;
} Prevention
- Send StartRequest exactly once per writer handle
- Route start handling only through the registration path
- Prefer warn-and-skip over unreachable! for protocol messages
When it happens
Trigger: A writer sends a second `StartRequest` after its handle is already registered and inserted, so the duplicate falls through `next_request_inner` into the `match` arm instead of being consumed by the registration path.
Common situations: Writer retry logic re-sending the start request after a timeout while the first one already registered the handle; bugs introduced when refactoring `next_event`'s select arms so start requests leak into the request stream; custom/test clients misusing the coordination protocol.
Related errors
- infinite
- should exist
- receive commit request on epoch {} from handle {} during alt
- receive AlignInitialEpoch on epoch {} from handle {} during
- receive AlignInitialEpoch after initialization
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/c145f8f781a97c57.
Report an issue: GitHub.