databendlabs/databend · error

sample size must be greater than zero

Error message

sample size must be greater than zero

What it means

FixedSizeSampler::new builds a reservoir sampler with capacity k, and internally represents k as a NonZeroUsize. This panic fires when a caller constructs a FixedSizeSampler with k == 0, which is meaningless for reservoir sampling (the sampler would never retain rows). The library treats a zero sample size as a programming error rather than a runtime condition.

Solutions

  1. Ensure the sample size passed to FixedSizeSampler::new is at least 1 before constructing the sampler.
  2. Validate the user-facing setting (e.g. sample size option) and return a proper error like ErrorCode::IllegalScalar for 0 instead of reaching the sampler.
  3. If 0 is legitimate, skip sampling entirely and use an empty result rather than constructing a sampler.

Example fix

// before
let sampler = FixedSizeSampler::new(sample_size, rng);
// after
assert!(sample_size > 0, "sample_size setting must be positive");
let sampler = FixedSizeSampler::new(sample_size.max(1), rng);
Defensive patterns

Strategy: validation

Validate before calling

if sample_size == 0 {
    return Err(ErrorCode::IllegalScalar("sample size must be greater than zero"));
}
let sampler = FixedSizeSampler::new(sample_size, rng);

Type guard

fn valid_sample_size(k: usize) -> bool { k > 0 }

Prevention

When it happens

Trigger: Calling FixedSizeSampler::new(0, rng), or passing a k value computed from configuration/other code that evaluates to 0 (e.g. an unset or empty sampling setting).

Common situations: A sampling size config option left at 0 or defaulted incorrectly; a query setting like SAMPLE_SIZE=0; an off-by-one computation of the requested sample size before constructing the sampler.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/f1ab44731de2d5f5. Report an issue: GitHub.

Appendix: source

Thrown at src/query/expression/src/sampler/fixed_size_sampler.rs:37

/// A fixed-capacity owning reservoir sampler using Vitter's Algorithm L.
///
/// Like Spark's `reservoirSampleAndCount`, the reservoir owns at most `k` values and also tracks
/// the input cardinality. Algorithm L replaces Spark's per-row Algorithm R decision with an exact
/// skip calculation, avoiding work for rows that cannot enter the reservoir. Input block boundaries
/// do not affect the resulting sample.
pub struct FixedSizeSampler<T, R: Rng> {
    samples: Vec<T>,
    k: usize,
    rows_seen: usize,
    // Zero-based global stream index selected next by Algorithm L.
    next_sample: Option<usize>,
    core: AlgoL<R>,
}

impl<T, R: Rng> FixedSizeSampler<T, R> {
    pub fn new(k: usize, rng: R) -> Self {
        let k = NonZeroUsize::new(k).expect("sample size must be greater than zero");
        Self {
            samples: Vec::with_capacity(k.get()),
            k: k.get(),
            rows_seen: 0,
            next_sample: None,
            core: AlgoL::new(k, rng),
        }
    }

    /// Consider one logical block while preserving the same result as one continuous row stream.
    ///
    /// `value_at` is evaluated only for rows entering the reservoir: every row during the initial
    /// fill, then only the rows selected by Algorithm L.
    pub fn add_block<F>(&mut self, rows: usize, mut value_at: F)
    where F: FnMut(usize) -> T {
        let start = self.rows_seen;
        let end = start.checked_add(rows).expect("sample row count overflow");
        let mut row = 0;

View on GitHub (pinned to 288d84d76e)