risingwavelabs/risingwave · error

unspecified type of `RangeFrameBound`

Error message

unspecified type of `RangeFrameBound`

What it means

RangeFrameBound::from_protobuf requires the bound type discriminant to be one of UnboundedPreceding/CurrentRow/UnboundedFollowing/Preceding/Following. An Unspecified type carries no meaning, so the conversion errors instead of defaulting.

Source

Thrown at src/expr/core/src/window_function/range.rs:236

    /// It's very similar to `first_curr_of`, just with everything on the other direction.
    pub fn last_curr_of(&self, order_value: impl ToOwnedDatum) -> Sentinelled<Datum> {
        self.start
            .for_calc()
            .reverse()
            .bound_of(order_value, self.order_type)
    }
}

pub type RangeFrameBound = FrameBound<RangeFrameOffset>;

impl RangeFrameBound {
    fn from_protobuf(
        bound: &PbRangeFrameBound,
        order_data_type: &DataType,
        offset_data_type: &DataType,
    ) -> Result<Self> {
        let bound = match bound.get_type()? {
            PbBoundType::Unspecified => bail!("unspecified type of `RangeFrameBound`"),
            PbBoundType::UnboundedPreceding => Self::UnboundedPreceding,
            PbBoundType::CurrentRow => Self::CurrentRow,
            PbBoundType::UnboundedFollowing => Self::UnboundedFollowing,
            bound_type @ (PbBoundType::Preceding | PbBoundType::Following) => {
                let offset_value = Datum::from_protobuf(bound.get_offset()?, offset_data_type)
                    .context("offset `Datum` is not decodable")?
                    .context("offset of `RangeFrameBound` must be non-NULL")?;
                let mut offset = RangeFrameOffset::new(offset_value);
                offset.prepare(order_data_type, offset_data_type)?;
                if bound_type == PbBoundType::Preceding {
                    Self::Preceding(offset)
                } else {
                    Self::Following(offset)
                }
            }
        };
        Ok(bound)
    }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Set the bound type explicitly in the frontend when constructing PbRangeFrameBound.
  2. Ensure the plan producer fills every frame bound's type before serialization.
  3. Inspect the serialized plan for unset oneof fields in frame bounds.

Example fix

// before
bound.set_type(PbBoundType::Unspecified);
// after
bound.set_type(PbBoundType::Preceding);
Defensive patterns

Strategy: validation

Validate before calling

fn bound_type_is_set(b: &PbRangeFrameBound) -> bool { b.get_type().map(|t| !matches!(t, PbBoundType::Unspecified)).unwrap_or(false) }

Type guard

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

Try / catch

let bound = match RangeFrameBound::from_protobuf(&pb_bound, &order_type, &offset_type) {
    Ok(b) => b,
    Err(e) => return Err(e.context("malformed RANGE frame bound in plan")),
};

Prevention

When it happens

Trigger: Deserializing a PbRangeFrameBound whose get_type() returns Unspecified — the bound type field was not set in the protobuf plan message.

Common situations: Frontend omitted the bound type when building the WindowFunction proto, an older protocol version, or hand-crafted test protobufs.

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/e708f0bf7f839e98. Report an issue: GitHub.