risingwavelabs/risingwave · error
unspecified AsOf join inequality type
Error message
unspecified AsOf join inequality type
What it means
AsOfJoinDesc::from_protobuf validates the inequality type carried in the serialized join plan. The protobuf enum AsOfJoinInequalityType must be one of Lt/Le/Gt/Ge; if the field is left at its default AsOfInequalityTypeUnspecified, the executor refuses to build the join because the AsOf comparison semantics are undefined. This is a deserialization-time plan validation error.
Source
Thrown at src/stream/src/executor/join/mod.rs:92
Ge,
Gt,
}
pub struct AsOfDesc {
pub left_idx: usize,
pub right_idx: usize,
pub inequality_type: AsOfInequalityType,
}
impl AsOfDesc {
pub fn from_protobuf(desc_proto: &AsOfJoinDesc) -> StreamResult<Self> {
let typ = match desc_proto.inequality_type() {
AsOfJoinInequalityType::AsOfInequalityTypeLt => AsOfInequalityType::Lt,
AsOfJoinInequalityType::AsOfInequalityTypeLe => AsOfInequalityType::Le,
AsOfJoinInequalityType::AsOfInequalityTypeGt => AsOfInequalityType::Gt,
AsOfJoinInequalityType::AsOfInequalityTypeGe => AsOfInequalityType::Ge,
AsOfJoinInequalityType::AsOfInequalityTypeUnspecified => {
bail!("unspecified AsOf join inequality type")
}
};
Ok(Self {
left_idx: desc_proto.left_idx as usize,
right_idx: desc_proto.right_idx as usize,
inequality_type: typ,
})
}
}
pub const fn is_outer_side(join_type: JoinTypePrimitive, side_type: SideTypePrimitive) -> bool {
join_type == JoinType::FullOuter
|| (join_type == JoinType::LeftOuter && side_type == SideType::Left)
|| (join_type == JoinType::RightOuter && side_type == SideType::Right)
}
pub const fn outer_side_null(join_type: JoinTypePrimitive, side_type: SideTypePrimitive) -> bool {
join_type == JoinType::FullOuterView on GitHub (pinned to 6469eb736d)
Solutions
- Ensure the frontend and stream compute nodes run matching versions so inequality_type is always serialized.
- Check the SQL: the AsOf join must specify a comparison (e.g. t1.ts <= t2.ts); rewrite the query with an explicit inequality operator.
- Inspect the generated plan proto to confirm inequality_type is set; if unset from a valid SQL query, fix the frontend planner to populate it.
- Update proto definitions / regenerate code if the deployment predates the field's introduction.
Example fix
// before: descriptor built without inequality type
let desc = AsOfJoinDescProto { left_idx, right_idx, ..Default::default() };
// after: always set an explicit inequality
let desc = AsOfJoinDescProto {
left_idx,
right_idx,
inequality_type: AsOfJoinInequalityType::AsOfInequalityTypeLe as i32,
..Default::default()
}; Defensive patterns
Strategy: validation
Validate before calling
// validate the proto before constructing the executor
let ty = desc_proto.inequality_type();
if ty == AsOfJoinInequalityType::AsOfInequalityTypeUnspecified {
return Err(anyhow!("AsOf join descriptor must set a concrete inequality_type (Lt/Le/Gt/Ge)"));
} Type guard
fn has_inequality(desc: &AsOfJoinDescProto) -> bool {
desc.inequality_type() != AsOfJoinInequalityType::AsOfInequalityTypeUnspecified
} Try / catch
let desc = AsOfJoinDesc::from_protobuf(&desc_proto)
.map_err(|e| StreamExecutorError::from(anyhow!("invalid AsOf join plan: {e:#}")))?; Prevention
- Keep frontend and stream compute node versions in lockstep to avoid proto field skew.
- Always set inequality_type explicitly when constructing AsOfJoinDescProto; never rely on the default.
- Add a planner-side assert that every AsOf join has a concrete comparison operator before serialization.
- Validate serialized plan protos in tests that build executor chains by hand.
When it happens
Trigger: Deserializing a StreamNode/executor proto for an AsOf join whose inequality descriptor has inequality_type = AS_OF_INEQUALITY_TYPE_UNSPECIFIED — i.e. the frontend/plan serializer produced a descriptor without setting the field, or an older client sends a proto written before the field existed.
Common situations: Version skew between frontend and stream compute binaries (old frontend proto lacking the inequality_type field); hand-crafted or test protos omitting the field; a frontend planner bug that forgets to set the inequality for temporal/AsOf joins.
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
- Failed to decode prost: field not found `{}`
- unspecified type of `WindowFrame`
- Failed to decode prost: field not found `{}`
- unknown MATCH_RECOGNIZE input mode: {}
- query_epoch not set in distributed lookup join
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/aceff0bc06d0d4f7.
Report an issue: GitHub.