{"record":{"id":"3dbf9a87b293d967","repo":"quarkusio/quarkus","slug":"timeout-must-be-positive-3dbf9a","errorCode":null,"errorMessage":"`timeout` must be positive","messagePattern":"`timeout` must be positive","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/SetArgs.java","lineNumber":32,"sourceCode":"\n    private long ex = -1;\n    private long exAt = -1;\n    private long px = -1;\n    private long pxAt = -1;\n    private boolean nx;\n    private boolean keepttl;\n    private boolean xx;\n    private boolean get;\n\n    /**\n     * Set the expiration timeout, in seconds.\n     *\n     * @param timeout expiration timeout in seconds\n     * @return the current {@code SetArgs}\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     * Set the expiration timeout, in seconds.\n     *\n     * @param timeout expiration timeout in seconds\n     * @return the current {@code SetArgs}\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.toSeconds());\n    }\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/SetArgs.java#L14-L50","documentation":"SetArgs.ex(long) sets the EXPIRE (seconds) option for Redis SET commands. The Quarkus Redis datasource validates arguments eagerly and throws IllegalArgumentException when timeout <= 0, since a non-positive expiration is meaningless to Redis and almost always indicates a caller bug (e.g. a zeroed or miscomputed value).","triggerScenarios":"Calling SetArgs.ex(0), SetArgs.ex(-1), or any ex(long) with a value <= 0, directly or via any value API that accepts SetArgs (e.g. RedisValueCommands.set(key, value, args)).","commonSituations":"Passing a computed TTL that evaluates to 0 because of unit-conversion mistakes (e.g. ms-to-seconds truncation of sub-second values), uninitialized config defaults of 0, or misreading the parameter as milliseconds.","solutions":["Ensure the timeout passed to ex(long) is >= 1 second before building the SetArgs.","If the intended TTL is sub-second, use px(Duration) / px(long) instead (milliseconds).","Check the config value or computation feeding the TTL; guard against 0/negative defaults."],"exampleFix":"// before\nSetArgs args = new SetArgs().ex(ttlMillis / 1000); // 500ms -> 0 -> throws\n// after\nSetArgs args = new SetArgs().px(Duration.ofMillis(ttlMillis));","handlingStrategy":"validation","validationCode":"if (ttlSeconds <= 0) {\n    throw new IllegalArgumentException(\"TTL must be at least 1 second, got: \" + ttlSeconds);\n}\nSetArgs args = new SetArgs().ex(ttlSeconds);","typeGuard":"static boolean isValidExTimeout(long timeout) {\n    return timeout > 0;\n}","tryCatchPattern":"try {\n    args = new SetArgs().ex(ttlSeconds);\n} catch (IllegalArgumentException e) {\n    log.warn(\"Invalid SET expiration \" + ttlSeconds + \", defaulting to 60s\");\n    args = new SetArgs().ex(60);\n}","preventionTips":["Validate TTL values at the configuration boundary before they reach Redis calls.","Use Duration types and px() for sub-second TTLs to avoid truncation to 0.","Add unit tests covering zero and negative TTL inputs."],"tags":["redis","argument-validation","illegal-argument"],"backgroundTag":"invalid-argument-value","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"}