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::FullOuter

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the frontend and stream compute nodes run matching versions so inequality_type is always serialized.
  2. Check the SQL: the AsOf join must specify a comparison (e.g. t1.ts <= t2.ts); rewrite the query with an explicit inequality operator.
  3. 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.
  4. 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

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


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