{"record":{"id":"a479ae452073de51","repo":"risingwavelabs/risingwave","slug":"hyperloglog-invalid-bucket-index","errorCode":null,"errorMessage":"HyperLogLog: Invalid bucket index","messagePattern":"HyperLogLog: Invalid bucket index","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/impl/src/aggregate/approx_count_distinct/append_only.rs","lineNumber":27,"sourceCode":"// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse risingwave_common::bail;\nuse risingwave_common_estimate_size::EstimateSize;\nuse risingwave_expr::Result;\n\nuse super::Bucket;\n\n#[derive(Clone, Copy, Default, Debug, EstimateSize, PartialEq, Eq)]\npub struct AppendOnlyBucket(pub u8);\n\nimpl Bucket for AppendOnlyBucket {\n    fn update(&mut self, index: u8, retract: bool) -> Result<()> {\n        if index > 64 || index == 0 {\n            bail!(\"HyperLogLog: Invalid bucket index\");\n        }\n        if retract {\n            bail!(\"HyperLogLog: Deletion in append-only bucket\");\n        }\n        if index > self.0 {\n            self.0 = index;\n        }\n        Ok(())\n    }\n\n    fn max(&self) -> u8 {\n        self.0\n    }\n}\n","sourceCodeStart":9,"sourceCodeEnd":42,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/aggregate/approx_count_distinct/append_only.rs#L9-L42","documentation":"The append-only HyperLogLog bucket stores a single u8 register holding the leading-zero count for one hash bucket. Valid ranks are 1..=64; index 0 and indices above 64 are impossible outputs of the hash/rank computation, so receiving one indicates corrupted hashing logic or state and the update aborts.","triggerScenarios":"`AppendOnlyBucket::update` called with `index == 0` or `index > 64` while feeding a new value into the approx_count_distinct aggregate.","commonSituations":"Custom/modified hash functions producing out-of-range ranks; memory corruption or deserialized aggregation state inconsistent with the current register width.","solutions":["Fix the rank computation to clamp/offset leading-zero counts into 1..=64 (e.g. `rank = zeros + 1`, capped at 64)","Verify the hash function width matches the HLL configuration (64-bit hashes expected)","Rebuild aggregation state if it came from an incompatible serialized format"],"exampleFix":"// before\nlet rank = hash.leading_zeros() as u8; // can be 0..=65\n// after\nlet rank = ((hash.leading_zeros() as u8) + 1).min(64);","handlingStrategy":"validation","validationCode":"fn valid_rank(index: u8) -> bool { (1..=64).contains(&index) }","typeGuard":"fn in_range(index: u8) -> bool { index != 0 && index <= 64 }","tryCatchPattern":"match bucket.update(index, false) {\n    Err(e) if e.to_string().contains(\"Invalid bucket index\") => return Err(anyhow!(\"hash rank out of range: {}\", index)),\n    other => other,\n}","preventionTips":["Cap rank computation at 64 and offset leading_zeros by +1","Use the standard 64-bit hash (e.g. xxhash/ahash 64) for HLL","Add assertion tests on rank range in hash unit tests"],"tags":["rust","hyperloglog","aggregate"],"backgroundTag":"value-out-of-range","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"}