risingwavelabs/risingwave · error

unspecified type of `RowsFrameBound`

Error message

unspecified type of `RowsFrameBound`

What it means

`RowsFrameBound::from_protobuf` matches the protobuf bound type enum exhaustively; the `Unspecified` variant (proto3 default for unset enum fields) is not a valid frame bound type, so it is rejected. This guards against deserializing a frame bound whose `type` field was never set.

Source

Thrown at src/expr/core/src/window_function/rows.rs:110

impl RowsFrameBound {
    pub(super) fn from_protobuf_legacy(bound: &PbBound) -> Result<Self> {
        use risingwave_pb::expr::window_frame::bound::PbOffset;

        let offset = bound.get_offset()?;
        let bound = match offset {
            PbOffset::Integer(offset) => Self::from_protobuf(&PbRowsFrameBound {
                r#type: bound.get_type()? as _,
                offset: Some(*offset),
            })?,
            PbOffset::Datum(_) => bail!("offset of `RowsFrameBound` must be `Integer`"),
        };
        Ok(bound)
    }

    fn from_protobuf(bound: &PbRowsFrameBound) -> Result<Self> {
        let bound = match bound.get_type()? {
            PbBoundType::Unspecified => bail!("unspecified type of `RowsFrameBound`"),
            PbBoundType::UnboundedPreceding => Self::UnboundedPreceding,
            PbBoundType::Preceding => Self::Preceding(*bound.get_offset()? as usize),
            PbBoundType::CurrentRow => Self::CurrentRow,
            PbBoundType::Following => Self::Following(*bound.get_offset()? as usize),
            PbBoundType::UnboundedFollowing => Self::UnboundedFollowing,
        };
        Ok(bound)
    }

    fn to_protobuf(&self) -> PbRowsFrameBound {
        let (r#type, offset) = match self {
            Self::UnboundedPreceding => (PbBoundType::UnboundedPreceding, None),
            Self::Preceding(offset) => (PbBoundType::Preceding, Some(*offset as _)),
            Self::CurrentRow => (PbBoundType::CurrentRow, None),
            Self::Following(offset) => (PbBoundType::Following, Some(*offset as _)),
            Self::UnboundedFollowing => (PbBoundType::UnboundedFollowing, None),
        };
        PbRowsFrameBound {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the frontend sets `r#type` to one of UnboundedPreceding/Preceding/CurrentRow/Following/UnboundedFollowing before serialization
  2. Regenerate the plan with a matching frontend version
  3. Validate the plan fragment before deserialization

Example fix

// before (proto)
// rows_frame_bound { offset: 3 }            // type unset -> UNSPECIFIED
// after (proto)
// rows_frame_bound { type: PRECEDING, offset: 3 }
Defensive patterns

Strategy: validation

Validate before calling

fn bound_type_set(t: i32) -> bool { t != 0 } // 0 = UNSPECIFIED

Type guard

fn is_specified(t: PbBoundType) -> bool { !matches!(t, PbBoundType::Unspecified) }

Try / catch

match RowsFrameBound::from_protobuf(&pb) {
    Ok(b) => b,
    Err(e) if e.to_string().contains("unspecified type") => return Err(anyhow!("frame bound type missing in plan")),
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Deserializing a `PbRowsFrameBound` where `r#type` is UNSPECIFIED (0) — i.e. the field was omitted or defaulted in the serialized plan.

Common situations: Protobuf messages constructed programmatically without setting the type field; older/other producers that don't populate required enums; corrupted plan fragments.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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