{"record":{"id":"aceff0bc06d0d4f7","repo":"risingwavelabs/risingwave","slug":"unspecified-asof-join-inequality-type-aceff0","errorCode":null,"errorMessage":"unspecified AsOf join inequality type","messagePattern":"unspecified AsOf join inequality type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/stream/src/executor/join/mod.rs","lineNumber":92,"sourceCode":"    Ge,\n    Gt,\n}\n\npub struct AsOfDesc {\n    pub left_idx: usize,\n    pub right_idx: usize,\n    pub inequality_type: AsOfInequalityType,\n}\n\nimpl AsOfDesc {\n    pub fn from_protobuf(desc_proto: &AsOfJoinDesc) -> StreamResult<Self> {\n        let typ = match desc_proto.inequality_type() {\n            AsOfJoinInequalityType::AsOfInequalityTypeLt => AsOfInequalityType::Lt,\n            AsOfJoinInequalityType::AsOfInequalityTypeLe => AsOfInequalityType::Le,\n            AsOfJoinInequalityType::AsOfInequalityTypeGt => AsOfInequalityType::Gt,\n            AsOfJoinInequalityType::AsOfInequalityTypeGe => AsOfInequalityType::Ge,\n            AsOfJoinInequalityType::AsOfInequalityTypeUnspecified => {\n                bail!(\"unspecified AsOf join inequality type\")\n            }\n        };\n        Ok(Self {\n            left_idx: desc_proto.left_idx as usize,\n            right_idx: desc_proto.right_idx as usize,\n            inequality_type: typ,\n        })\n    }\n}\n\npub const fn is_outer_side(join_type: JoinTypePrimitive, side_type: SideTypePrimitive) -> bool {\n    join_type == JoinType::FullOuter\n        || (join_type == JoinType::LeftOuter && side_type == SideType::Left)\n        || (join_type == JoinType::RightOuter && side_type == SideType::Right)\n}\n\npub const fn outer_side_null(join_type: JoinTypePrimitive, side_type: SideTypePrimitive) -> bool {\n    join_type == JoinType::FullOuter","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/stream/src/executor/join/mod.rs#L74-L110","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: descriptor built without inequality type\nlet desc = AsOfJoinDescProto { left_idx, right_idx, ..Default::default() };\n// after: always set an explicit inequality\nlet desc = AsOfJoinDescProto {\n    left_idx,\n    right_idx,\n    inequality_type: AsOfJoinInequalityType::AsOfInequalityTypeLe as i32,\n    ..Default::default()\n};","handlingStrategy":"validation","validationCode":"// validate the proto before constructing the executor\nlet ty = desc_proto.inequality_type();\nif ty == AsOfJoinInequalityType::AsOfInequalityTypeUnspecified {\n    return Err(anyhow!(\"AsOf join descriptor must set a concrete inequality_type (Lt/Le/Gt/Ge)\"));\n}","typeGuard":"fn has_inequality(desc: &AsOfJoinDescProto) -> bool {\n    desc.inequality_type() != AsOfJoinInequalityType::AsOfInequalityTypeUnspecified\n}","tryCatchPattern":"let desc = AsOfJoinDesc::from_protobuf(&desc_proto)\n    .map_err(|e| StreamExecutorError::from(anyhow!(\"invalid AsOf join plan: {e:#}\")))?;","preventionTips":["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."],"tags":["streaming","protobuf","asof-join","deserialization","rust"],"backgroundTag":"invalid-enum-value","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}