{"record":{"id":"73fcc262714c8ca2","repo":"risingwavelabs/risingwave","slug":"hyperloglog-invalid-bucket-index-73fcc2","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/updatable.rs","lineNumber":99,"sourceCode":"    }\n}\n\nimpl EstimateSize for SparseCount {\n    fn estimated_heap_size(&self) -> usize {\n        self.inner.capacity() * std::mem::size_of::<(u8, u64)>()\n    }\n}\n\n#[derive(Clone, Debug, EstimateSize, PartialEq, Eq)]\npub(super) struct UpdatableBucket<const DENSE_BITS: usize = 16> {\n    dense_counts: [u64; DENSE_BITS],\n    sparse_counts: SparseCount,\n}\n\nimpl<const DENSE_BITS: usize> UpdatableBucket<DENSE_BITS> {\n    fn get_bucket(&self, index: u8) -> Result<u64> {\n        if index > 64 || index == 0 {\n            bail!(\"HyperLogLog: Invalid bucket index\");\n        }\n\n        if index > DENSE_BITS as u8 {\n            Ok(self.sparse_counts.get(index))\n        } else {\n            Ok(self.dense_counts[index as usize - 1])\n        }\n    }\n}\n\nimpl<const DENSE_BITS: usize> Default for UpdatableBucket<DENSE_BITS> {\n    fn default() -> Self {\n        Self {\n            dense_counts: [0u64; DENSE_BITS],\n            sparse_counts: SparseCount::new(),\n        }\n    }\n}","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/aggregate/approx_count_distinct/updatable.rs#L81-L117","documentation":"`UpdatableBucket::get_bucket` validates the rank index before reading from either the dense counter array or the sparse counter map. Valid ranks are 1..=64 (u64 registers); index 0 or >64 would index out of bounds or read nonexistent sparse entries, so it's rejected as an invariant violation.","triggerScenarios":"`UpdatableBucket::update` (or get/put paths) invoked with `index == 0` or `index > 64`, meaning the rank computed from the hash is outside the representable range.","commonSituations":"Modified or inconsistent rank computation (`leading_zeros` not offset/capped); deserialized state from an incompatible register width (DENSE_BITS mismatch).","solutions":["Fix rank derivation so it yields 1..=64: `((hash.leading_zeros() as u8) + 1).min(64)`","Confirm the hash produces 64-bit values consistent with HLL parameters","Rebuild aggregation state if serialized with different DENSE_BITS"],"exampleFix":"// before\nlet index = (value.leading_zeros()) as u8; // may be 0\n// after\nlet index = ((value.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, retract) {\n    Err(e) if e.to_string().contains(\"Invalid bucket index\") => return Err(anyhow!(\"HLL rank {} out of 1..=64\", index)),\n    other => other,\n}","preventionTips":["Clamp computed ranks to 1..=64 before update","Ensure hash width matches DENSE_BITS configuration","Test rank generation over representative hash inputs"],"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"}