{"record":{"id":"76963a2e5fe186c6","repo":"openzipkin/zipkin","slug":"ttl-0","errorCode":null,"errorMessage":"ttl <= 0","messagePattern":"ttl <= 0","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"zipkin/src/main/java/zipkin2/internal/DelayLimiter.java","lineNumber":44,"sourceCode":"     * expires.\n     */\n    public Builder ttl(long ttl, TimeUnit ttlUnit) {\n      if (ttlUnit == null) throw new NullPointerException(\"ttlUnit == null\");\n      this.ttl = ttl;\n      this.ttlUnit = ttlUnit;\n      return this;\n    }\n\n    /**\n     * This bounds suppressions, useful because contexts can be accidentally unlimited cardinality.\n     */\n    public Builder cardinality(int cardinality) {\n      this.cardinality = cardinality;\n      return this;\n    }\n\n    public <C> DelayLimiter<C> build() {\n      if (ttl <= 0L) throw new IllegalArgumentException(\"ttl <= 0\");\n      if (cardinality <= 0) throw new IllegalArgumentException(\"cardinality <= 0\");\n      return new DelayLimiter<>(new SuppressionFactory(ttlUnit.toNanos(ttl)), cardinality);\n    }\n\n    Builder() {\n    }\n  }\n\n  final SuppressionFactory suppressionFactory;\n  final ConcurrentHashMap<C, Suppression<C>> cache = new ConcurrentHashMap<>();\n  final DelayQueue<Suppression<C>> suppressions = new DelayQueue<>();\n  final int cardinality;\n\n  DelayLimiter(SuppressionFactory suppressionFactory, int cardinality) {\n    this.suppressionFactory = suppressionFactory;\n    this.cardinality = cardinality;\n  }\n","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin/src/main/java/zipkin2/internal/DelayLimiter.java#L26-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nDelayLimiter<C> limiter = DelayLimiter.newBuilder().cardinality(10_000).build();\n\n// after\nDelayLimiter<C> limiter = DelayLimiter.newBuilder()\n    .ttl(cfg.ttlMillis > 0 ? cfg.ttlMillis : 1, TimeUnit.MILLISECONDS)\n    .cardinality(10_000)\n    .build();","handlingStrategy":"validation","validationCode":"if (cfg.ttlMillis <= 0) throw new ConfigException(\"delayLimiter.ttlMillis must be > 0\");\nDelayLimiter<C> limiter = DelayLimiter.newBuilder()\n    .ttl(cfg.ttlMillis, TimeUnit.MILLISECONDS).cardinality(n).build();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call ttl(...) before build(); the 0 default means 'unconfigured'.","Validate ttl/cardinality properties at startup so misconfiguration fails fast."],"tags":["zipkin","internal","delay-limiter","validation","builder"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}