openzipkin/zipkin · error · IllegalArgumentException
ttl <= 0
Error message
ttl <= 0
What it means
DelayLimiter.Builder.build() throws IllegalArgumentException('ttl <= 0') when the configured TTL is zero or negative (defaults to 0, so a builder that never calls ttl() fails here too). A suppression window of non-positive length is meaningless — shouldInvoke would never suppress, so the builder forces you to configure a real duration. A companion check rejects cardinality <= 0.
Source
Thrown at zipkin/src/main/java/zipkin2/internal/DelayLimiter.java:44
* expires.
*/
public Builder ttl(long ttl, TimeUnit ttlUnit) {
if (ttlUnit == null) throw new NullPointerException("ttlUnit == null");
this.ttl = ttl;
this.ttlUnit = ttlUnit;
return this;
}
/**
* This bounds suppressions, useful because contexts can be accidentally unlimited cardinality.
*/
public Builder cardinality(int cardinality) {
this.cardinality = cardinality;
return this;
}
public <C> DelayLimiter<C> build() {
if (ttl <= 0L) throw new IllegalArgumentException("ttl <= 0");
if (cardinality <= 0) throw new IllegalArgumentException("cardinality <= 0");
return new DelayLimiter<>(new SuppressionFactory(ttlUnit.toNanos(ttl)), cardinality);
}
Builder() {
}
}
final SuppressionFactory suppressionFactory;
final ConcurrentHashMap<C, Suppression<C>> cache = new ConcurrentHashMap<>();
final DelayQueue<Suppression<C>> suppressions = new DelayQueue<>();
final int cardinality;
DelayLimiter(SuppressionFactory suppressionFactory, int cardinality) {
this.suppressionFactory = suppressionFactory;
this.cardinality = cardinality;
}
View on GitHub (pinned to 878ce2a1fa)
Solutions
- Set a positive ttl before build(), e.g. .ttl(1, TimeUnit.MILLISECONDS) minimum.
- Check the property wiring: missing/renamed keys silently leave the 0 default — validate at startup that the parsed ttl > 0.
- If the limiter is optional in your context, only build it when a positive ttl is configured.
Example fix
// before
DelayLimiter<C> limiter = DelayLimiter.newBuilder().cardinality(10_000).build();
// after
DelayLimiter<C> limiter = DelayLimiter.newBuilder()
.ttl(cfg.ttlMillis > 0 ? cfg.ttlMillis : 1, TimeUnit.MILLISECONDS)
.cardinality(10_000)
.build(); Defensive patterns
Strategy: validation
Validate before calling
if (cfg.ttlMillis <= 0) throw new ConfigException("delayLimiter.ttlMillis must be > 0");
DelayLimiter<C> limiter = DelayLimiter.newBuilder()
.ttl(cfg.ttlMillis, TimeUnit.MILLISECONDS).cardinality(n).build(); Prevention
- Always call ttl(...) before build(); the 0 default means 'unconfigured'.
- Validate ttl/cardinality properties at startup so misconfiguration fails fast.
When it happens
Trigger: Calling build() without ever calling ttl(...), or with ttl(0, ...) / a negative config value; commonly after a config property was renamed or not loaded, leaving ttl at its 0 default.
Common situations: Internal storage components (e.g. service/endpoint lookup limiting) configured from properties where the ttl key is missing, misnamed, or parsed as 0 after a version upgrade; test builders that only set cardinality.
Related errors
AI-assisted analysis of openzipkin/zipkin@878ce2a1fa (2026-08-14).
Data as JSON: /api/errors/76963a2e5fe186c6.
Report an issue: GitHub.