opendataloader-project/opendataloader-pdf · error · IllegalStateException

Failed to convert via Hancom AI

Error message

Failed to convert via Hancom AI

What it means

convertAsync runs convert() on a CompletableFuture; checked IOException cannot propagate out of supplyAsync, so it is wrapped as IllegalStateException with the real IOException as cause. This is the Hancom AI equivalent of error 45 — the message is generic ('Failed to convert via Hancom AI') because the cause carries the actionable detail (DLA empty, pdf2img failure, etc.).

Source

Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HancomAIClient.java:276

    }

    /**
     * Creates a PageImageCache based on config.
     */
    private PageImageCache createPageImageCache() throws IOException {
        if ("disk".equalsIgnoreCase(config.getImageCache())) {
            return new DiskPageImageCache();
        }
        return new MemoryPageImageCache();
    }

    @Override
    public CompletableFuture<HybridResponse> convertAsync(HybridRequest request) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return convert(request);
            } catch (IOException e) {
                throw new IllegalStateException("Failed to convert via Hancom AI", e);
            }
        });
    }

    /**
     * Captions each Figure found by DLA:
     * 1. Get page images via pdf2img
     * 2. Find Figure objects (label 10) from DLA results
     * 3. Crop each Figure from page image
     * 4. Send cropped image to IMAGE_CAPTIONING
     *
     * @return ArrayNode of {page_number, object_id, bbox, caption}
     */
    private ArrayNode captionFigures(byte[] pdfBytes, JsonNode dlaResult,
                                     PageImageCache pageImageCache, CropOutput cropOutput) {
        ArrayNode captions = objectMapper.createArrayNode();

        // Extract pages from DLA result

View on GitHub (pinned to a7789b8e77)

Solutions

  1. Inspect the cause: ((IllegalStateException)e).getCause() for the real IOException
  2. Prefer synchronous convert() if you handle checked IOException directly
  3. Call checkAvailability() before convertAsync to fail fast
  4. Add retry with backoff for transient backend failures

Example fix

// before
HybridResponse r = client.convertAsync(req).join();
// after
HybridResponse r = client.convertAsync(req)
    .exceptionally(e -> {
        Throwable c = (e instanceof CompletionException && e.getCause() != null) ? e.getCause() : e;
        throw new RuntimeException("hancom convert failed: " + c.getMessage(), c);
    }).join();
Defensive patterns

Strategy: try-catch

Validate before calling

// Fail fast before the async call
client.checkAvailability();

Try / catch

client.convertAsync(request)
    .exceptionally(e -> {
        Throwable cause = (e instanceof CompletionException && e.getCause() != null)
            ? e.getCause() : e;
        // cause is the real IOException
        log.error("Hancom convert failed: {}", cause.getMessage(), cause);
        return null; // or trigger Java fallback
    });

Prevention

When it happens

Trigger: Calling HancomAIClient.convertAsync(request) and the underlying convert() throws IOException (DLA empty result, pdf2img HTTP error, page image fetch failure that escapes per-page handling, etc.).

Common situations: Using the async API and not inspecting getCause(); backend becoming unavailable mid-pipeline after the health check passed; a page-image failure that is not caught by the per-page handlers.

Related errors


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