opendataloader-project/opendataloader-pdf · error · IllegalArgumentException

Timeout must be non-negative: %s

Error message

Timeout must be non-negative: %s

What it means

HybridConfig.setTimeoutMs() rejects any value below zero. A timeout of 0 means 'no timeout' (infinite wait), which is the default (DEFAULT_TIMEOUT_MS = 0). Negative timeouts are nonsensical for OkHttp's timeout configuration and would cause undefined behavior in the underlying HTTP client, so they are rejected eagerly with the offending value in the message.

Source

Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HybridConfig.java:121

    /**
     * Gets the request timeout in milliseconds.
     *
     * @return The timeout in milliseconds.
     */
    public int getTimeoutMs() {
        return timeoutMs;
    }

    /**
     * Sets the request timeout in milliseconds. Use 0 for no timeout.
     *
     * @param timeoutMs The timeout in milliseconds (0 = no timeout).
     * @throws IllegalArgumentException if timeout is negative.
     */
    public void setTimeoutMs(int timeoutMs) {
        if (timeoutMs < 0) {
            throw new IllegalArgumentException("Timeout must be non-negative: " + timeoutMs);
        }
        this.timeoutMs = timeoutMs;
    }

    /**
     * Checks if fallback to Java processing is enabled when backend fails.
     *
     * @return true if fallback is enabled, false otherwise.
     */
    public boolean isFallbackToJava() {
        return fallbackToJava;
    }

    /**
     * Sets whether to fallback to Java processing when backend fails.
     *
     * @param fallbackToJava true to enable fallback, false to fail on backend error.
     */

View on GitHub (pinned to a7789b8e77)

Solutions

  1. Use 0 for no timeout (infinite wait), or any positive integer for a specific millisecond timeout.
  2. Validate the input before calling setTimeoutMs: `if (timeout < 0) throw new IllegalArgumentException(...)`.
  3. Check the CLI argument or config file for a stray negative sign.
  4. If the timeout is computed dynamically, clamp it: `setTimeoutMs(Math.max(0, computedTimeout))`.

Example fix

// before: computed timeout can go negative
config.setTimeoutMs(targetMs - elapsedMs);

// after: clamp to minimum of 0
config.setTimeoutMs(Math.max(0, targetMs - elapsedMs));
Defensive patterns

Strategy: validation

Validate before calling

int timeout = parseTimeout(configSource); // from CLI, config file, or env
if (timeout < 0) {
    throw new IllegalArgumentException(
        "Timeout must be >= 0 (0 = no timeout). Got: " + timeout);
}
config.setTimeoutMs(timeout);

Type guard

public static boolean isValidTimeout(int timeoutMs) {
    return timeoutMs >= 0;
}

Try / catch

try {
    config.setTimeoutMs(requestedTimeout);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Timeout must be non-negative")) {
        // Clamp to 0 (no timeout) rather than failing
        config.setTimeoutMs(0);
        LOGGER.warning("Negative timeout clamped to 0 (no timeout)");
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling config.setTimeoutMs(-1) or any negative integer. This can originate from a CLI parsing error (e.g., --hybrid-timeout -1), arithmetic underflow in a config builder, or a misconfigured properties file where the timeout is computed as a difference that goes negative.

Common situations: CLI argument `--hybrid-timeout -500` (user error or script bug); timeout calculated as (someBaseline - someOffset) where offset exceeds baseline; reading from a YAML/JSON config where the value is accidentally negative; environment variable parsed as integer but containing a negative number.

Understand the failure class

Related errors


AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14). Data as JSON: /api/errors/304d22415d2864e5. Report an issue: GitHub.