opendataloader-project/opendataloader-pdf · error · IllegalArgumentException

threads must be >= 1, got {threads}

Error message

threads must be >= 1, got {threads}

What it means

Config.setThreads rejects any value < 1 with IllegalArgumentException, then caps the stored value to Runtime.getRuntime().availableProcessors(). The CLI path (applyThreadsOption) validates the requested value is >= 1 before calling setThreads, so this guard mainly fires for programmatic API callers passing 0 or a negative int. Passing 0 to mean 'auto' is not supported.

Source

Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/api/Config.java:930

    private boolean outputStdout = false;

    public boolean isOutputStdout() {
        return outputStdout;
    }

    public void setOutputStdout(boolean outputStdout) {
        this.outputStdout = outputStdout;
    }

    private int threads = 1;

    public int getThreads() {
        return threads;
    }

    public void setThreads(int threads) {
        if (threads < 1) {
            throw new IllegalArgumentException("threads must be >= 1, got " + threads);
        }
        this.threads = Math.min(threads, Runtime.getRuntime().availableProcessors());
    }

    /**
     * Returns true if any output format requires structured content
     * (reading order, heading levels, list detection, etc.).
     * Text-only output does not need these expensive processing steps.
     */
    public boolean needsStructuredProcessing() {
        return isGenerateMarkdown() || isGenerateHtml() || isGenerateJSON() || isGeneratePDF();
    }

    /**
     * Resolves conflicts between individually valid option values.
     * Call once after all setters, before passing the Config to a processor.
     * Currently: in hybrid mode, forces {@code threads} to 1 because the hybrid
     * pipeline runs sequentially regardless of this value.

View on GitHub (pinned to a7789b8e77)

Solutions

  1. Pass an integer >= 1.
  2. For 'auto' semantics, pass Runtime.getRuntime().availableProcessors() (setThreads will not increase it beyond that anyway).
  3. Guard computed counts: Math.max(1, cores - reserve).

Example fix

// before: config.setThreads(0);   // intended 'auto'
// after:  config.setThreads(Runtime.getRuntime().availableProcessors());
Defensive patterns

Strategy: validation

Validate before calling

int requested = computeThreads();
if (requested < 1) {
    requested = Runtime.getRuntime().availableProcessors(); // 'auto' fallback
}
config.setThreads(requested);

Type guard

static boolean isValidThreads(int n) { return n >= 1; }

Try / catch

try {
    config.setThreads(n);
} catch (IllegalArgumentException e) {
    config.setThreads(1); // safe default
}

Prevention

When it happens

Trigger: Direct API call config.setThreads(0) or config.setThreads(-2); computing threads from a formula that can underflow to <= 0 (e.g. cores - 1 when cores == 1).

Common situations: Assuming 0 means 'use all cores'; deriving thread count from availableProcessors() - reserve and hitting zero on a single-core container.

Related errors


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