t8y2/dbx · error · IllegalArgumentException

Query timeout cannot be negative: " + timeoutSecs

Error message

Query timeout cannot be negative: " + timeoutSecs

What it means

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).

Source

Thrown at agents/drivers/oceanbase-oracle/src/main/java/com/dbx/agent/oceanbaseoracle/OceanBaseOracleAgent.java:121

        }
    }

    @Override
    protected Object resultValue(ResultSet rs, int index, int sqlType) {
        switch (sqlType) {
            case Types.BINARY:
            case Types.VARBINARY:
            case Types.LONGVARBINARY:
            case Types.BLOB:
                return unchecked(() -> JdbcExecutor.stringResultValue(rs, index, sqlType));
            default:
                return super.resultValue(rs, index, sqlType);
        }
    }

    static String queryTimeoutSql(int timeoutSecs) {
        if (timeoutSecs < 0) {
            throw new IllegalArgumentException("Query timeout cannot be negative: " + timeoutSecs);
        }
        long timeoutMicros = timeoutSecs == 0
            ? UNLIMITED_QUERY_TIMEOUT_MICROS
            : timeoutSecs * MICROS_PER_SECOND;
        return "ALTER SESSION SET ob_query_timeout = " + timeoutMicros;
    }

    private static boolean isReadOnlyTransactionError(SQLException error) {
        Deque<Throwable> pending = new ArrayDeque<>();
        Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<>());
        pending.add(error);
        while (!pending.isEmpty()) {
            Throwable current = pending.removeFirst();
            if (!seen.add(current)) {
                continue;
            }
            if (current instanceof SQLException) {
                SQLException sqlError = (SQLException) current;

View on GitHub (pinned to c0390bff16)

Solutions

  1. Pass 0 for unlimited instead of a negative value, or clamp: Math.max(0, timeoutSecs).
  2. Translate the driver-independent -1 sentinel to 0 at the call site before invoking the hooks.
  3. Fix the config/env source feeding the timeout so it never yields negatives.

Example fix

// before
agent.beforeQueryExecution(sql, timeoutSecs); // timeoutSecs = -1
// after
agent.beforeQueryExecution(sql, timeoutSecs < 0 ? 0 : timeoutSecs); // 0 = unlimited
Defensive patterns

Strategy: validation

Validate before calling

int safeTimeout(int timeoutSecs) {
    if (timeoutSecs < 0) throw new IllegalArgumentException("Pass 0 for unlimited, not a negative value: " + timeoutSecs);
    return timeoutSecs;
}

Try / catch

try {
    agent.beforeQueryExecution(sql, timeoutSecs);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Query timeout cannot be negative")) {
        agent.beforeQueryExecution(sql, 0); // 0 = unlimited
    } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/233bf1a2027326b3. Report an issue: GitHub.