openzipkin/zipkin · error · IllegalArgumentException

rate should be between 0 and 1: was {}

Error message

rate should be between 0 and 1: was {}

What it means

CollectorSampler.create(float) validates the sample rate is within [0,1]; outside that range it throws IllegalArgumentException with the offending value in the message. The rate is converted to a boundary on Long.MAX_VALUE for trace-id-based sampling, so values like 1.5 or a percentage like 10 (meaning 10%) are rejected.

Source

Thrown at zipkin-collector/core/src/main/java/zipkin2/collector/CollectorSampler.java:34

 *
 * <p>Accepts a percentage of trace ids by comparing their absolute value against a potentially
 * dynamic boundary. eg {@code isSampled == abs(traceId) <= boundary}
 *
 * <p>While idempotent, this implementation's sample rate won't exactly match the input rate because
 * trace ids are not perfectly distributed across 64bits. For example, tests have shown an error
 * rate of 3% when 100K trace ids are {@link java.util.Random#nextLong random}.
 */
public abstract class CollectorSampler {
  public static final CollectorSampler ALWAYS_SAMPLE = CollectorSampler.create(1.0f);

  /**
   * Returns a trace ID sampler with the indicated rate.
   *
   * @param rate minimum sample rate is 0.0001, or 0.01% of traces
   */
  public static CollectorSampler create(float rate) {
    if (rate < 0 || rate > 1)
      throw new IllegalArgumentException("rate should be between 0 and 1: was " + rate);
    final long boundary = (long) (Long.MAX_VALUE * rate); // safe cast as less <= 1
    return new CollectorSampler() {
      @Override
      protected long boundary() {
        return boundary;
      }
    };
  }

  protected abstract long boundary();

  /**
   * Returns true if spans with this trace ID should be recorded to storage.
   *
   * <p>Zipkin v1 allows storage-layer sampling, which can help prevent spikes in traffic from
   * overloading the system. Debug spans are always stored.
   *
   * <p>This uses only the lower 64 bits of the trace ID as instrumentation still send mixed trace

View on GitHub (pinned to 878ce2a1fa)

Solutions

  1. Express the rate as a fraction in [0,1]: 10% sampling → 0.1f.
  2. If your config is a percentage, divide by 100 before calling create.
  3. Treat 'unset' sentinels (-1) by falling back to ALWAYS_SAMPLE or a documented default instead of forwarding them.

Example fix

// before
float rate = Float.parseFloat(env.get("COLLECTOR_SAMPLE_PERCENTAGE")); // e.g. "10"
CollectorSampler.create(rate); // throws 'rate should be between 0 and 1: was 10.0'

// after
float pct = Float.parseFloat(env.get("COLLECTOR_SAMPLE_PERCENTAGE"));
CollectorSampler.create(pct / 100.0f); // 10 -> 0.1
Defensive patterns

Strategy: validation

Validate before calling

java
float rate = Float.parseFloat(pct) / 100f;
if (rate < 0f || rate > 1f) throw new IllegalArgumentException("sample rate must be 0-100%: " + pct);
return CollectorSampler.create(rate);

Prevention

When it happens

Trigger: Calling CollectorSampler.create(rate) with rate < 0 or > 1 — the classic case is passing a percentage (e.g. 10 for 10%, or 0.1 for 0.1%) instead of a fraction (0.1 for 10%).

Common situations: Env var COLLECTOR_SAMPLE_PERCENTAGE=10 piped straight into create(); confusion between basis points, percent, and probability; a default of -1 used as 'unset' sentinel reaching the sampler.

Related errors


AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14). Data as JSON: /api/errors/40e82538a95e4b6f. Report an issue: GitHub.