risingwavelabs/risingwave · error

HyperLogLog: Count exceeds maximum bucket value.Your data st

Error message

HyperLogLog: Count exceeds maximum bucket value.Your data stream may have too many repeated values or too large acardinality for approx_count_distinct to handle (max: 2^64 - 1)

What it means

Each dense bucket in the updatable HyperLogLog stores its counter in a u64. When a non-retract update tries to increment a bucket already at u64::MAX, the addition would overflow, so the library refuses instead of silently wrapping. This implies an astronomically large number of repeated register hits (max cardinality 2^64 - 1), so it practically indicates corrupted or pathological state.

Source

Thrown at src/expr/impl/src/aggregate/approx_count_distinct/updatable.rs:132

            sparse_counts: SparseCount::new(),
        }
    }
}

impl<const DENSE_BITS: usize> Bucket for UpdatableBucket<DENSE_BITS> {
    fn update(&mut self, index: u8, retract: bool) -> Result<()> {
        if index > 64 || index == 0 {
            bail!("HyperLogLog: Invalid bucket index");
        }

        let count = self.get_bucket(index)?;

        if !retract {
            if index > DENSE_BITS as u8 {
                self.sparse_counts.add(index);
            } else if index >= 1 {
                if count == u64::MAX {
                    bail!(
                        "HyperLogLog: Count exceeds maximum bucket value.\
                        Your data stream may have too many repeated values or too large a\
                        cardinality for approx_count_distinct to handle (max: 2^64 - 1)"
                    );
                }
                self.dense_counts[index as usize - 1] = count + 1;
            }
        } else {
            // We don't have to worry about the user deleting nonexistent elements, so the counts
            // can never go below 0.
            if index > DENSE_BITS as u8 {
                self.sparse_counts.subtract(index);
            } else if index >= 1 {
                self.dense_counts[index as usize - 1] = count - 1;
            }
        }

        Ok(())

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Rebuild the materialized view / aggregation state to reset bucket counters.
  2. Check for state corruption from crash recovery or version migration and re-backfill the aggregation.
  3. File an issue if this reproduces with normal data volumes; real streams cannot legitimately hit this bound.
Defensive patterns

Strategy: validation

Validate before calling

// before incrementing, check the bucket
if !retract && bucket_count == u64::MAX {
    return Err(anyhow!("bucket {} saturated", index));
}

Type guard

fn can_increment(count: u64) -> bool { count < u64::MAX }

Prevention

When it happens

Trigger: Calling `update(index, retract=false)` on a bucket whose current count equals u64::MAX; i.e. the same index incremented 2^64-1 times or a bucket pre-loaded with u64::MAX via merge/restore.

Common situations: Corrupted aggregation state in a materialized view; a merge of sketches with poisoned registers; extremely long-lived streaming jobs accumulating in one bucket (theoretical only).

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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