risingwavelabs/risingwave · error
offset of `RowsFrameBound` must be `Integer`
Error message
offset of `RowsFrameBound` must be `Integer`
What it means
The legacy protobuf `RowsFrameBound` carried offsets as a oneof: either an Integer or a Datum. ROWS frames count rows, so the offset must be an integer row count; a Datum offset is only meaningful for RANGE frames. The decoder rejects Datum offsets explicitly.
Source
Thrown at src/expr/core/src/window_function/rows.rs:103
impl FrameBoundsImpl for RowsFrameBounds {
fn validate(&self) -> Result<()> {
FrameBound::validate_bounds(&self.start, &self.end, |_| Ok(()))
}
}
pub type RowsFrameBound = FrameBound<usize>;
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 {View on GitHub (pinned to 6469eb736d)
Solutions
- Regenerate the plan so the frontend emits the offset as `PbOffset::Integer`
- If you must handle Datum offsets, convert them to integers before deserialization or use the non-legacy path
- Verify frontend/backend versions agree on frame bound encoding
Example fix
// before: datum offset in RowsFrameBound proto
// offset { datum { ... } }
// after: integer offset
// offset { integer: 3 } Defensive patterns
Strategy: validation
Validate before calling
fn valid_rows_offset(b: &PbRowsFrameBoundLegacy) -> bool {
matches!(b.offset, Some(PbOffset::Integer(_)))
} Type guard
fn has_integer_offset(o: &PbOffset) -> bool { matches!(o, PbOffset::Integer(_)) } Try / catch
match RowsFrameBound::from_protobuf_legacy(&pb) {
Ok(b) => b,
Err(e) if e.to_string().contains("must be `Integer`") => return Err(anyhow!("ROWS frame offset must be an integer literal")),
Err(e) => return Err(e.into()),
} Prevention
- Always emit integer offsets for ROWS frames in the frontend
- Avoid the legacy oneof encoding; use the current PbRowsFrameBound
- Validate plan fragments before deserialization
When it happens
Trigger: Calling `from_protobuf_legacy` on a `RowsFrameBound` whose protobuf oneof `offset` is set to the `Datum` variant instead of `Integer`.
Common situations: Plan fragments produced by an old or altered frontend that encoded the frame offset as a literal datum; hand-crafted or corrupted protobuf payloads.
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
- unspecified type of `WindowFrame`
- unspecified type of `RowsFrameBound`
- query_epoch not set in distributed lookup join
- Failed to decode prost: field not found `{}`
- Invalid datetime: seconds: {secs}, nanoseconds: {nsecs}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/4f1b742d844cbe06.
Report an issue: GitHub.