{"record":{"id":"aa69fbf7c23cbd64","repo":"elastic/elasticsearch","slug":"staletime-must-be-a-positive-duration-but-was-s","errorCode":null,"errorMessage":"staleTime must be a positive duration but was [${staleTime}]","messagePattern":"staleTime must be a positive duration but was \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/workload-identity-aws/src/main/java/org/elasticsearch/workload/identity/aws/AsyncWebIdentityCredentialsProvider.java","lineNumber":95,"sourceCode":"    private final Consumer<ActionListener<String>> tokenSupplier;\n    private final StsAsyncClient stsAsyncClient;\n    private final Duration prefetchTime;\n    private final Duration staleTime;\n    private final Clock clock;\n\n    private final AtomicReference<Cached> cache = new AtomicReference<>();\n    private final AtomicReference<CompletableFuture<Cached>> inFlight = new AtomicReference<>();\n\n    private AsyncWebIdentityCredentialsProvider(Builder builder) {\n        this.roleArn = Objects.requireNonNull(builder.roleArn, \"roleArn must not be null\");\n        this.roleSessionName = Objects.requireNonNull(builder.roleSessionName, \"roleSessionName must not be null\");\n        this.tokenSupplier = Objects.requireNonNull(builder.tokenSupplier, \"tokenSupplier must not be null\");\n        this.stsAsyncClient = Objects.requireNonNull(builder.stsAsyncClient, \"stsAsyncClient must not be null\");\n        this.prefetchTime = builder.prefetchTime != null ? builder.prefetchTime : DEFAULT_PREFETCH_TIME;\n        this.staleTime = builder.staleTime != null ? builder.staleTime : DEFAULT_STALE_TIME;\n        this.clock = builder.clock != null ? builder.clock : Clock.systemUTC();\n        if (staleTime.isNegative() || staleTime.isZero()) {\n            throw new IllegalArgumentException(\"staleTime must be a positive duration but was [\" + staleTime + \"]\");\n        }\n        if (prefetchTime.isNegative() || prefetchTime.isZero()) {\n            throw new IllegalArgumentException(\"prefetchTime must be a positive duration but was [\" + prefetchTime + \"]\");\n        }\n        // prefetchTime must start no later than staleTime, otherwise prefetchAt would fall after staleAt and the\n        // background-refresh window in resolveIdentity() would be unreachable.\n        if (prefetchTime.compareTo(staleTime) < 0) {\n            throw new IllegalArgumentException(\n                \"prefetchTime [\" + prefetchTime + \"] must be greater than or equal to staleTime [\" + staleTime + \"]\"\n            );\n        }\n    }\n\n    public static Builder builder() {\n        return new Builder();\n    }\n\n    @Override","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/workload-identity-aws/src/main/java/org/elasticsearch/workload/identity/aws/AsyncWebIdentityCredentialsProvider.java#L77-L113","documentation":"The AsyncWebIdentityCredentialsProvider builder rejects a staleTime Duration that is zero or negative. staleTime defines how long before credential expiry a cached entry is considered stale and triggers refresh; a non-positive value would never allow staleness. The check runs in the private constructor after defaults are applied.","triggerScenarios":"Building the provider with .staleTime(Duration.ofSeconds(0)) or a negative Duration. The default (DEFAULT_STALE_TIME) is used when null, so this only fires when an explicit non-positive Duration is supplied.","commonSituations":"Computing staleTime from a config knob typed as 0; passing Duration.ZERO meaning \"refresh immediately\"; mis-tuning refresh windows; arithmetic that produces a negative duration when expiry is near.","solutions":["Set staleTime to a positive Duration (e.g. Duration.ofMinutes(5))","If you want aggressive refresh, use a small positive value, not zero","Validate config-sourced durations before passing to the builder"],"exampleFix":"// before\n.provider.staleTime(Duration.ZERO).build()\n// after\n.provider.staleTime(Duration.ofSeconds(30)).build()","handlingStrategy":"validation","validationCode":"if (staleTime == null || staleTime.isZero() || staleTime.isNegative()) {\n    throw new IllegalArgumentException(\"staleTime must be positive: \" + staleTime);\n}\nbuilder.staleTime(staleTime);","typeGuard":"static boolean isPositiveDuration(Duration d) {\n    return d != null && !d.isZero() && !d.isNegative();\n}","tryCatchPattern":"try { builder.staleTime(d).build(); }\ncatch (IllegalArgumentException e) { /* use default */ }","preventionTips":["Treat 0 as invalid, not as 'always refresh'","Validate config-sourced durations before builder call","Document the units (Duration) in the config key"],"tags":["aws","workload-identity","credentials","config","startup"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}