risingwavelabs/risingwave · error · ExprError::Internal

expected RowNumberState, got {other:?}

Error message

expected RowNumberState, got {other:?}

What it means

`RowNumber` window function state is deserialized from a protobuf `FunctionState`. If the received state variant is anything other than `FunctionState::RowNumberState`, the state does not match the function and deserialization fails with this internal error.

Source

Thrown at src/expr/impl/src/window_function/rank.rs:73

impl RankFuncCount for RowNumber {
    fn count(&mut self, _curr_key: StateKey) -> i64 {
        let curr_rank = self.prev_rank + 1;
        self.prev_rank = curr_rank;
        curr_rank
    }

    fn to_proto_state(&self) -> FunctionState {
        FunctionState::RowNumberState(RowNumberState {
            prev_rank: self.prev_rank,
        })
    }

    fn from_proto_state(state: FunctionState) -> Result<Self> {
        match state {
            FunctionState::RowNumberState(s) => Ok(Self {
                prev_rank: s.prev_rank,
            }),
            other => Err(ExprError::Internal(anyhow::anyhow!(
                "expected RowNumberState, got {other:?}"
            ))),
        }
    }
}

#[derive(EstimateSize)]
pub(super) struct Rank {
    prev_order_key: Option<MemcmpEncoded>,
    prev_rank: i64,
    // 1-based position of the previously output row within its peer group.
    // Used to advance the rank by the peer-group size when a new group starts.
    prev_pos_in_peer_group: i64,
}

impl Default for Rank {
    fn default() -> Self {
        Self {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Full-rebuild / recreate the materialized view so state matches the current function kind
  2. Verify the state key maps to the correct function type in the streaming plan
  3. Check protobuf FunctionState encoding for version mismatch bugs

Example fix

// before
other => Err(ExprError::Internal(anyhow::anyhow!("expected RowNumberState, got {other:?}"))),
// after
FunctionState::RowNumberState(_) | FunctionState::RankState(_) => ... // handle variants explicitly, or add a default fallback
Defensive patterns

Strategy: try-catch

Validate before calling

fn state_matches(state: &FunctionState) -> bool { matches!(state, FunctionState::RowNumberState(_)) }

Type guard

fn as_row_number_state(s: &FunctionState) -> Option<&RowNumberState> { match s { FunctionState::RowNumberState(x) => Some(x), _ => None } }

Try / catch

match RowNumber::from_proto_state(state) {
    Err(e) if e.to_string().contains("expected RowNumberState") => rebuild_mv_state(),
    other => other?,
}

Prevention

When it happens

Trigger: `RowNumber::from_proto_state` receiving a FunctionState of a different variant (e.g. RankState or DenseRankState), typically from a mismatched state type written under the same state key during recovery or a bug in state serialization.

Common situations: Stream recovery after a schema/state version change; swapping a window function kind in a materialized view without full rebuild; corrupted or mismatched checkpoint state.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/8e061c7879f53e9e. Report an issue: GitHub.