{"record":{"id":"04091e7a248e018d","repo":"openzipkin/zipkin","slug":"ttlunit-null","errorCode":null,"errorMessage":"ttlUnit == null","messagePattern":"ttlUnit == null","errorType":"exception","errorClass":"NullPointerException","httpStatus":null,"severity":"error","filePath":"zipkin/src/main/java/zipkin2/internal/DelayLimiter.java","lineNumber":29,"sourceCode":"\n/** Limits invocations of a given context to at most once per period. */\n// this is a dependency-free variant formerly served by an expiring guava cache\npublic final class DelayLimiter<C> {\n  public static Builder newBuilder() {\n    return new Builder();\n  }\n\n  public static final class Builder {\n    long ttl = 0L;\n    TimeUnit ttlUnit = TimeUnit.MILLISECONDS;\n    int cardinality = 0;\n\n    /**\n     * When {@link #shouldInvoke(Object)} returns true, it will return false until this duration\n     * 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    }","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin/src/main/java/zipkin2/internal/DelayLimiter.java#L11-L47","documentation":"DelayLimiter.Builder.ttl(long, TimeUnit) throws NullPointerException('ttlUnit == null') when the time unit argument is null. The ttl value itself may be anything at this point; only the unit is null-checked at setter time because the duration cannot be interpreted without it. DelayLimiter is an internal rate-suppression helper used by zipkin storage components.","triggerScenarios":"Calling .ttl(ttl, unit) where unit came from a config lookup, map.get, or enum valueOf that returned null (e.g. TimeUnit.valueOf(\"MILLIS\") with wrong case throws earlier, but a Map<String,TimeUnit> miss returns null silently).","commonSituations":"Config-driven code that parses a unit string from properties/YAML into a TimeUnit and passes the result unchecked; Optional-style chains where the unit is only sometimes set.","solutions":["Default the unit when absent: unit != null ? unit : TimeUnit.MILLISECONDS.","Validate the parsed unit string at config load time with a whitelist (ms/s/m) and fail fast there.","Use TimeUnit.valueOf(name.toUpperCase(Locale.ROOT)) guarded by try-catch or a Set of valid names."],"exampleFix":"// before\nbuilder.ttl(ttl, unitByName.get(cfg.unit)); // null on unknown name\n\n// after\nTimeUnit unit = unitByName.getOrDefault(cfg.unit, TimeUnit.MILLISECONDS);\nbuilder.ttl(ttl, unit);","handlingStrategy":"validation","validationCode":"TimeUnit unit = configured != null ? configured : TimeUnit.MILLISECONDS;\nbuilder.ttl(ttl, unit);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Map config strings to TimeUnit via a whitelist (ms/s/m) with a default.","Fail fast on unknown unit names at config load, not inside builders."],"tags":["zipkin","internal","delay-limiter","null-pointer","builder"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}