{"record":{"id":"9c9c0a555dd77c77","repo":"opendataloader-project/opendataloader-pdf","slug":"threads-must-be-1-got-threads","errorCode":null,"errorMessage":"threads must be >= 1, got {threads}","messagePattern":"threads must be >= 1, got (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/api/Config.java","lineNumber":930,"sourceCode":"    private boolean outputStdout = false;\n\n    public boolean isOutputStdout() {\n        return outputStdout;\n    }\n\n    public void setOutputStdout(boolean outputStdout) {\n        this.outputStdout = outputStdout;\n    }\n\n    private int threads = 1;\n\n    public int getThreads() {\n        return threads;\n    }\n\n    public void setThreads(int threads) {\n        if (threads < 1) {\n            throw new IllegalArgumentException(\"threads must be >= 1, got \" + threads);\n        }\n        this.threads = Math.min(threads, Runtime.getRuntime().availableProcessors());\n    }\n\n    /**\n     * Returns true if any output format requires structured content\n     * (reading order, heading levels, list detection, etc.).\n     * Text-only output does not need these expensive processing steps.\n     */\n    public boolean needsStructuredProcessing() {\n        return isGenerateMarkdown() || isGenerateHtml() || isGenerateJSON() || isGeneratePDF();\n    }\n\n    /**\n     * Resolves conflicts between individually valid option values.\n     * Call once after all setters, before passing the Config to a processor.\n     * Currently: in hybrid mode, forces {@code threads} to 1 because the hybrid\n     * pipeline runs sequentially regardless of this value.","sourceCodeStart":912,"sourceCodeEnd":948,"githubUrl":"https://github.com/opendataloader-project/opendataloader-pdf/blob/a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8/java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/api/Config.java#L912-L948","documentation":"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.","triggerScenarios":"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).","commonSituations":"Assuming 0 means 'use all cores'; deriving thread count from availableProcessors() - reserve and hitting zero on a single-core container.","solutions":["Pass an integer >= 1.","For 'auto' semantics, pass Runtime.getRuntime().availableProcessors() (setThreads will not increase it beyond that anyway).","Guard computed counts: Math.max(1, cores - reserve)."],"exampleFix":"// before: config.setThreads(0);   // intended 'auto'\n// after:  config.setThreads(Runtime.getRuntime().availableProcessors());","handlingStrategy":"validation","validationCode":"int requested = computeThreads();\nif (requested < 1) {\n    requested = Runtime.getRuntime().availableProcessors(); // 'auto' fallback\n}\nconfig.setThreads(requested);","typeGuard":"static boolean isValidThreads(int n) { return n >= 1; }","tryCatchPattern":"try {\n    config.setThreads(n);\n} catch (IllegalArgumentException e) {\n    config.setThreads(1); // safe default\n}","preventionTips":["Never pass 0 to mean 'auto'; pass availableProcessors() instead.","Guard derived counts with Math.max(1, cores - reserve)."],"tags":["configuration","validation","threads","parallelism"],"backgroundTag":null,"analyzedSha":"a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8","analyzedAt":"2026-08-14T05:22:03.953Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}