opendataloader-project/opendataloader-pdf · error · IOException

Hancom upload failed with status %s: %s

Error message

Hancom upload failed with status %s: %s

What it means

The Hancom file upload endpoint (POST to UPLOAD_ENDPOINT) returned an HTTP status code outside the 2xx range. The error message includes both the status code and the raw response body string for diagnostics. This is a server-side rejection of the upload — authentication failure, malformed request, rate limiting, or internal server error.

Source

Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HancomClient.java:193

     * @throws IOException If the upload fails.
     */
    private String uploadFile(byte[] pdfBytes) throws IOException {
        MultipartBody requestBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("file", DEFAULT_FILENAME,
                RequestBody.create(pdfBytes, MEDIA_TYPE_PDF))
            .build();

        Request request = new Request.Builder()
            .url(baseUrl + UPLOAD_ENDPOINT)
            .post(requestBody)
            .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                ResponseBody body = response.body();
                String bodyStr = body != null ? body.string() : "";
                throw new IOException("Hancom upload failed with status " + response.code() + ": " + bodyStr);
            }

            ResponseBody body = response.body();
            if (body == null) {
                throw new IOException("Empty response body from upload");
            }

            JsonNode root = objectMapper.readTree(body.string());
            // Response format: {"codeNum":0,"code":"file.upload.success","data":{"fileId":"...",...}}
            JsonNode dataNode = root.get("data");
            if (dataNode == null) {
                throw new IOException("Invalid upload response: missing data field");
            }
            JsonNode fileIdNode = dataNode.get("fileId");
            if (fileIdNode == null || !fileIdNode.isTextual()) {
                throw new IOException("Invalid upload response: missing fileId in data");
            }

View on GitHub (pinned to a7789b8e77)

Solutions

  1. Inspect the response body in the error message — Hancom returns JSON like {"codeNum":<n>,"code":"<reason>"} with a human-readable failure code.
  2. For 401/403: verify your API credentials are set and current for the Hancom endpoint.
  3. For 413: reduce PDF size (split the document or lower scan DPI) or raise the server's upload limit.
  4. For 429: implement exponential backoff with retry (add a delay between upload attempts).
  5. For 5xx: retry after a short delay; if persistent, check the Hancom service status page or contact support.
  6. Run the same upload via curl to isolate whether it's a client-side or server-side issue: `curl -X POST -H 'Content-Type: multipart/form-data' -F 'file=@doc.pdf' <upload-url>`.

Example fix

// before: single attempt, no retry on transient server errors
String fileId = client.uploadFile(pdfBytes);

// after: retry on 5xx with exponential backoff
String fileId = null;
for (int attempt = 0; attempt < 3; attempt++) {
    try {
        fileId = client.uploadFile(pdfBytes);
        break;
    } catch (IOException e) {
        if (!e.getMessage().contains("status 5") || attempt == 2) throw e;
        Thread.sleep((1L << attempt) * 1000);
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// Validate PDF before upload
if (pdfBytes == null || pdfBytes.length == 0) {
    throw new IllegalArgumentException("Cannot upload empty PDF");
}
// Check PDF signature
if (pdfBytes.length < 5 || pdfBytes[0] != '%' || pdfBytes[1] != 'P') {
    throw new IllegalArgumentException("File does not start with PDF signature");
}

Try / catch

try {
    fileId = client.uploadFile(pdfBytes);
} catch (IOException e) {
    String msg = e.getMessage();
    if (msg.contains("status 429") || msg.contains("status 5")) {
        // Transient — retry with backoff
        Thread.sleep(2000);
        fileId = client.uploadFile(pdfBytes);
    } else if (msg.contains("status 401") || msg.contains("status 403")) {
        // Auth failure — do not retry, fix credentials
        throw new SecurityException("Authentication failed for Hancom upload", e);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling HancomClient.uploadFile(pdfBytes) when the server rejects the multipart upload. Specific triggers: 401/403 (missing or expired API key/auth token), 413 (PDF exceeds server's max upload size), 415 (unsupported media type — the Content-Type header is wrong), 429 (rate limit exceeded), 500/502/503 (server-side processing crash or upstream dependency failure).

Common situations: API key not configured or expired; attempting to upload an extremely large PDF that exceeds the server limit; server is under maintenance or overloaded; the PDF file is corrupt and the server's intake validation rejects it; rate limiting during batch processing of many documents.

Related errors


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