{"record":{"id":"88d34061a1976cfd","repo":"quarkusio/quarkus","slug":"timeout-must-be-positive","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/string/SetArgs.java","lineNumber":35,"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 specified expire time, in seconds.\n     *\n     * @param timeout expire time in seconds.\n     * @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","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/SetArgs.java#L17-L53","documentation":"SetArgs.ex(long) sets the EX (seconds) option for a Redis SET command; the client requires a strictly positive value. Redis itself rejects non-positive expire times, so the Quarkus Redis client fails fast with IllegalArgumentException. Zero and negative values are both invalid.","triggerScenarios":"Calling SetArgs.ex(0) or ex(-n), usually from a timeout variable initialized to 0 or computed as a negative difference of timestamps.","commonSituations":"A configured TTL defaults to 0 meaning 'no expiry', but the code calls ex(0) unconditionally instead of skipping ex(); or a subtraction like (deadline - now) yields 0 or negative when the deadline already passed.","solutions":["Pass a value >= 1 second; clamp with Math.max(1, timeoutSeconds) if a minimum TTL is acceptable","Only call ex() when the timeout is > 0: build args conditionally","Fix the source of the value: validate configuration so a TTL of 0 means 'skip ex()' rather than reaching the setter"],"exampleFix":"// before\nSetArgs args = SetArgs.args().ex(ttlSeconds); // throws when ttlSeconds <= 0\n\n// after\nSetArgs args = ttlSeconds > 0\n    ? SetArgs.args().ex(ttlSeconds)\n    : SetArgs.args();","handlingStrategy":"validation","validationCode":"if (ttlSeconds <= 0) {\n    throw new IllegalArgumentException(\"TTL must be positive, got: \" + ttlSeconds);\n}\nSetArgs.args().ex(ttlSeconds);","typeGuard":null,"tryCatchPattern":"try {\n    args.ex(timeout);\n} catch (IllegalArgumentException e) {\n    if (!e.getMessage().contains(\"`timeout` must be positive\")) throw e;\n    args = SetArgs.args(); // proceed without expiry\n}","preventionTips":["Treat TTL config value 0 as 'no expiry' and build args conditionally","Validate TTL configuration at startup with @ConfigMapping validation","Watch for negative results when computing TTL from timestamp differences"],"tags":["redis","illegal-argument","validation","ttl"],"backgroundTag":"invalid-ttl-value","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}