{"record":{"id":"233bf1a2027326b3","repo":"t8y2/dbx","slug":"query-timeout-cannot-be-negative-timeoutsecs","errorCode":null,"errorMessage":"Query timeout cannot be negative: \" + timeoutSecs","messagePattern":"Query timeout cannot be negative: \" \\+ timeoutSecs","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"agents/drivers/oceanbase-oracle/src/main/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgent.java","lineNumber":121,"sourceCode":"        }\n    }\n\n    @Override\n    protected Object resultValue(ResultSet rs, int index, int sqlType) {\n        switch (sqlType) {\n            case Types.BINARY:\n            case Types.VARBINARY:\n            case Types.LONGVARBINARY:\n            case Types.BLOB:\n                return unchecked(() -> JdbcExecutor.stringResultValue(rs, index, sqlType));\n            default:\n                return super.resultValue(rs, index, sqlType);\n        }\n    }\n\n    static String queryTimeoutSql(int timeoutSecs) {\n        if (timeoutSecs < 0) {\n            throw new IllegalArgumentException(\"Query timeout cannot be negative: \" + timeoutSecs);\n        }\n        long timeoutMicros = timeoutSecs == 0\n            ? UNLIMITED_QUERY_TIMEOUT_MICROS\n            : timeoutSecs * MICROS_PER_SECOND;\n        return \"ALTER SESSION SET ob_query_timeout = \" + timeoutMicros;\n    }\n\n    private static boolean isReadOnlyTransactionError(SQLException error) {\n        Deque<Throwable> pending = new ArrayDeque<>();\n        Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<>());\n        pending.add(error);\n        while (!pending.isEmpty()) {\n            Throwable current = pending.removeFirst();\n            if (!seen.add(current)) {\n                continue;\n            }\n            if (current instanceof SQLException) {\n                SQLException sqlError = (SQLException) current;","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/oceanbase-oracle/src/main/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgent.java#L103-L139","documentation":"OceanBaseOracleAgent validates the query timeout before building the ALTER SESSION statement. A negative timeoutSecs would produce a meaningless negative microsecond session timeout, so queryTimeoutSql throws IllegalArgumentException. 0 is allowed and means unlimited (UNLIMITED_QUERY_TIMEOUT_MICROS).","triggerScenarios":"Calling queryTimeoutSql (directly or via beforeQueryExecution / beforePooledConnectionReturn hooks) with timeoutSecs < 0, e.g. passing a sentinel like -1 as 'default' instead of 0 for unlimited.","commonSituations":"Config files where the timeout field defaults to -1 meaning 'unset' and the value is forwarded unconverted; subtracting timestamps to compute a remaining timeout that has already elapsed; mixing conventions where -1 means unlimited in another driver but 0 means unlimited here.","solutions":["Pass 0 for unlimited instead of a negative value, or clamp: Math.max(0, timeoutSecs).","Translate the driver-independent -1 sentinel to 0 at the call site before invoking the hooks.","Fix the config/env source feeding the timeout so it never yields negatives."],"exampleFix":"// before\nagent.beforeQueryExecution(sql, timeoutSecs); // timeoutSecs = -1\n// after\nagent.beforeQueryExecution(sql, timeoutSecs < 0 ? 0 : timeoutSecs); // 0 = unlimited","handlingStrategy":"validation","validationCode":"int safeTimeout(int timeoutSecs) {\n    if (timeoutSecs < 0) throw new IllegalArgumentException(\"Pass 0 for unlimited, not a negative value: \" + timeoutSecs);\n    return timeoutSecs;\n}","typeGuard":null,"tryCatchPattern":"try {\n    agent.beforeQueryExecution(sql, timeoutSecs);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Query timeout cannot be negative\")) {\n        agent.beforeQueryExecution(sql, 0); // 0 = unlimited\n    } else throw e;\n}","preventionTips":["Remember this driver's convention: 0 = unlimited; never forward -1 sentinels.","Clamp values from config/env with Math.max(0, value) before use.","Add a unit test for queryTimeoutSql boundary values (-1, 0, large)."],"tags":["oceanbase","oracle","validation","timeout","argument-check"],"backgroundTag":"invalid-query-timeout","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}