{"record":{"id":"f935a407b2a0a100","repo":"quarkusio/quarkus","slug":"timeout-must-not-be-null-f935a4","errorCode":null,"errorMessage":"`timeout` must not be `null`","messagePattern":"`timeout` must not be `null`","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/SetArgs.java","lineNumber":49,"sourceCode":"     * @return the current {@code GetExArgs}\n     */\n    public SetArgs ex(long timeout) {\n        if (timeout <= 0) {\n            throw new IllegalArgumentException(\"`timeout` must be positive\");\n        }\n        this.ex = timeout;\n        return this;\n    }\n\n    /**\n     * Sets the expiration.\n     *\n     * @param timeout expire time in seconds.\n     * @return the current {@code GetExArgs}\n     */\n    public SetArgs ex(Duration timeout) {\n        if (timeout == null) {\n            throw new IllegalArgumentException(\"`timeout` must not be `null`\");\n        }\n        return ex(timeout.toMillis() / 1000);\n    }\n\n    /**\n     * Sets the expiration time\n     *\n     * @param timestamp the timestamp\n     * @return the current {@code GetExArgs}\n     */\n    public SetArgs exAt(long timestamp) {\n        this.exAt = timestamp;\n        return this;\n    }\n\n    /**\n     * Sets the expiration time\n     *","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/SetArgs.java#L31-L67","documentation":"SetArgs.ex(Duration) converts the duration to seconds and delegates to ex(long), but first rejects a null Duration. The Quarkus Redis client validates arguments eagerly to fail at the call site rather than at Redis. Null cannot represent an expiry duration.","triggerScenarios":"Calling SetArgs ex(null), commonly when a Duration is read from optional configuration (e.g. ConfigProvider or Optional<Duration>) and not defaulted.","commonSituations":"Config property quarkus.redis-style TTL is absent, producing a null Duration; or a method parameter of type Duration is optional and the caller passes null to signal 'no expiry'.","solutions":["Provide a default: ex(duration != null ? duration : Duration.ofSeconds(60))","Build args conditionally and skip ex() when duration == null","Switch to the long-based ex(long seconds) only after the duration has been resolved and null-checked"],"exampleFix":"// before\nDuration ttl = config.ttl(); // may be null\nSetArgs args = SetArgs.args().ex(ttl); // throws\n\n// after\nDuration ttl = config.ttl();\nSetArgs args = ttl != null\n    ? SetArgs.args().ex(ttl)\n    : SetArgs.args();","handlingStrategy":"validation","validationCode":"if (ttl == null || ttl.isZero() || ttl.isNegative()) {\n    throw new IllegalArgumentException(\"TTL duration must be non-null and positive\");\n}\nSetArgs.args().ex(ttl);","typeGuard":"boolean isPositiveDuration(Duration d) {\n    return d != null && !d.isZero() && !d.isNegative();\n}","tryCatchPattern":"try {\n    args.ex(duration);\n} catch (IllegalArgumentException e) {\n    if (!e.getMessage().contains(\"`timeout` must not be `null`\")) throw e;\n    args = SetArgs.args(); // skip expiry\n}","preventionTips":["Provide defaults for optional Duration config values at mapping time","Use @ConfigMapping with defaults instead of raw nullable Duration lookups","Check null and sign of computed Durations before passing to Redis arg builders"],"tags":["redis","null-argument","illegal-argument","validation"],"backgroundTag":"null-required-argument","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}