opendataloader-project/opendataloader-pdf · error · IllegalStateException
Failed to convert
Error message
Failed to convert
What it means
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.
Source
Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/DoclingFastServerClient.java:149
}
@Override
public HybridResponse convert(HybridRequest request) throws IOException {
Request httpRequest = buildConvertRequest(request);
LOGGER.log(Level.FINE, "Sending request to {0}", baseUrl + CONVERT_ENDPOINT);
try (Response response = httpClient.newCall(httpRequest).execute()) {
return parseResponse(response);
}
}
@Override
public CompletableFuture<HybridResponse> convertAsync(HybridRequest request) {
return CompletableFuture.supplyAsync(() -> {
try {
return convert(request);
} catch (IOException e) {
throw new IllegalStateException("Failed to convert", e);
}
});
}
/**
* Gets the base URL of this client.
*
* @return The base URL.
*/
public String getBaseUrl() {
return baseUrl;
}
/**
* Builds a multipart/form-data HTTP request for the convert endpoint.
*/
private Request buildConvertRequest(HybridRequest request) {
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder()View on GitHub (pinned to a7789b8e77)
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
Example fix
// before
CompletableFuture<HybridResponse> f = client.convertAsync(req);
HybridResponse r = f.join(); // throws bare IllegalStateException
// after
HybridResponse r = client.convertAsync(req)
.exceptionally(e -> {
Throwable cause = (e instanceof CompletionException && e.getCause() != null) ? e.getCause() : e;
throw new RuntimeException("convert failed: " + cause.getMessage(), cause);
}).join(); Defensive patterns
Strategy: try-catch
Validate before calling
// Fail fast with a clearer message before the async call client.checkAvailability(); // throws a precise IOException if unreachable
Try / catch
client.convertAsync(request)
.whenComplete((resp, ex) -> {
if (ex != null) {
Throwable cause = (ex instanceof CompletionException && ex.getCause() != null)
? ex.getCause() : ex;
// cause is the real IOException (IllegalStateException wraps it)
log.error("convert failed: {}", cause.getMessage(), cause);
}
}); Prevention
- 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
When it happens
Trigger: 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.).
Common situations: 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.
Related errors
- Failed to convert via Hancom AI
- Failed to convert
- Hybrid server is not available at %s To start the local hybr
- Hybrid server at %s returned HTTP %s during health check. Th
- Docling Fast Server request failed with status %s: %s
AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14).
Data as JSON: /api/errors/4187cc3827aa2654.
Report an issue: GitHub.