{"record":{"id":"4187cc3827aa2654","repo":"opendataloader-project/opendataloader-pdf","slug":"failed-to-convert","errorCode":null,"errorMessage":"Failed to convert","messagePattern":"Failed to convert","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/DoclingFastServerClient.java","lineNumber":149,"sourceCode":"    }\n\n    @Override\n    public HybridResponse convert(HybridRequest request) throws IOException {\n        Request httpRequest = buildConvertRequest(request);\n        LOGGER.log(Level.FINE, \"Sending request to {0}\", baseUrl + CONVERT_ENDPOINT);\n\n        try (Response response = httpClient.newCall(httpRequest).execute()) {\n            return parseResponse(response);\n        }\n    }\n\n    @Override\n    public CompletableFuture<HybridResponse> convertAsync(HybridRequest request) {\n        return CompletableFuture.supplyAsync(() -> {\n            try {\n                return convert(request);\n            } catch (IOException e) {\n                throw new IllegalStateException(\"Failed to convert\", e);\n            }\n        });\n    }\n\n    /**\n     * Gets the base URL of this client.\n     *\n     * @return The base URL.\n     */\n    public String getBaseUrl() {\n        return baseUrl;\n    }\n\n    /**\n     * Builds a multipart/form-data HTTP request for the convert endpoint.\n     */\n    private Request buildConvertRequest(HybridRequest request) {\n        MultipartBody.Builder bodyBuilder = new MultipartBody.Builder()","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/opendataloader-project/opendataloader-pdf/blob/a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8/java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/DoclingFastServerClient.java#L131-L167","documentation":"convertAsync runs convert() on a CompletableFuture; checked IOException cannot propagate out of supplyAsync, so it is wrapped as IllegalStateException with cause set to the real IOException. The wrapped cause (any of the docling convert/parse failures) holds the actionable detail, and the message 'Failed to convert' is intentionally generic because the cause message carries the specifics.","triggerScenarios":"Calling DoclingFastServerClient.convertAsync(request) and the underlying convert() throws IOException (network failure on /v1/convert/file, non-2xx status, malformed response, missing document field, etc.).","commonSituations":"Using the async API and not inspecting getCause(); the server becoming unavailable mid-run; transient network errors surfacing only on the convert call after health check passed.","solutions":["Inspect the exception chain: future.exceptionally(e -> ...) and read ((IllegalStateException)e).getCause() for the real IOException","Prefer the synchronous convert() if you can handle checked IOException directly","Add retry/backoff around convertAsync for transient failures","Call checkAvailability() before convertAsync to fail fast with a clearer message"],"exampleFix":"// before\nCompletableFuture<HybridResponse> f = client.convertAsync(req);\nHybridResponse r = f.join(); // throws bare IllegalStateException\n// after\nHybridResponse r = client.convertAsync(req)\n    .exceptionally(e -> {\n        Throwable cause = (e instanceof CompletionException && e.getCause() != null) ? e.getCause() : e;\n        throw new RuntimeException(\"convert failed: \" + cause.getMessage(), cause);\n    }).join();","handlingStrategy":"try-catch","validationCode":"// Fail fast with a clearer message before the async call\nclient.checkAvailability(); // throws a precise IOException if unreachable","typeGuard":null,"tryCatchPattern":"client.convertAsync(request)\n    .whenComplete((resp, ex) -> {\n        if (ex != null) {\n            Throwable cause = (ex instanceof CompletionException && ex.getCause() != null)\n                ? ex.getCause() : ex;\n            // cause is the real IOException (IllegalStateException wraps it)\n            log.error(\"convert failed: {}\", cause.getMessage(), cause);\n        }\n    });","preventionTips":["Always unwrap getCause() on exceptions from convertAsync","Prefer synchronous convert() when you can handle checked IOException","Call checkAvailability() first to get a precise failure message"],"tags":["hybrid","docling-fast","async","completable-future"],"backgroundTag":null,"analyzedSha":"a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8","analyzedAt":"2026-08-14T05:22:03.953Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}