{"record":{"id":"02efd28b243f6057","repo":"opendataloader-project/opendataloader-pdf","slug":"hancom-upload-failed-with-status-s-s","errorCode":null,"errorMessage":"Hancom upload failed with status %s: %s","messagePattern":"Hancom upload failed with status (.+?): (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HancomClient.java","lineNumber":193,"sourceCode":"     * @throws IOException If the upload fails.\n     */\n    private String uploadFile(byte[] pdfBytes) throws IOException {\n        MultipartBody requestBody = new MultipartBody.Builder()\n            .setType(MultipartBody.FORM)\n            .addFormDataPart(\"file\", DEFAULT_FILENAME,\n                RequestBody.create(pdfBytes, MEDIA_TYPE_PDF))\n            .build();\n\n        Request request = new Request.Builder()\n            .url(baseUrl + UPLOAD_ENDPOINT)\n            .post(requestBody)\n            .build();\n\n        try (Response response = httpClient.newCall(request).execute()) {\n            if (!response.isSuccessful()) {\n                ResponseBody body = response.body();\n                String bodyStr = body != null ? body.string() : \"\";\n                throw new IOException(\"Hancom upload failed with status \" + response.code() + \": \" + bodyStr);\n            }\n\n            ResponseBody body = response.body();\n            if (body == null) {\n                throw new IOException(\"Empty response body from upload\");\n            }\n\n            JsonNode root = objectMapper.readTree(body.string());\n            // Response format: {\"codeNum\":0,\"code\":\"file.upload.success\",\"data\":{\"fileId\":\"...\",...}}\n            JsonNode dataNode = root.get(\"data\");\n            if (dataNode == null) {\n                throw new IOException(\"Invalid upload response: missing data field\");\n            }\n            JsonNode fileIdNode = dataNode.get(\"fileId\");\n            if (fileIdNode == null || !fileIdNode.isTextual()) {\n                throw new IOException(\"Invalid upload response: missing fileId in data\");\n            }\n","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/opendataloader-project/opendataloader-pdf/blob/a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8/java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HancomClient.java#L175-L211","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Inspect the response body in the error message — Hancom returns JSON like {\"codeNum\":<n>,\"code\":\"<reason>\"} with a human-readable failure code.","For 401/403: verify your API credentials are set and current for the Hancom endpoint.","For 413: reduce PDF size (split the document or lower scan DPI) or raise the server's upload limit.","For 429: implement exponential backoff with retry (add a delay between upload attempts).","For 5xx: retry after a short delay; if persistent, check the Hancom service status page or contact support.","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>`."],"exampleFix":"// before: single attempt, no retry on transient server errors\nString fileId = client.uploadFile(pdfBytes);\n\n// after: retry on 5xx with exponential backoff\nString fileId = null;\nfor (int attempt = 0; attempt < 3; attempt++) {\n    try {\n        fileId = client.uploadFile(pdfBytes);\n        break;\n    } catch (IOException e) {\n        if (!e.getMessage().contains(\"status 5\") || attempt == 2) throw e;\n        Thread.sleep((1L << attempt) * 1000);\n    }\n}","handlingStrategy":"retry","validationCode":"// Validate PDF before upload\nif (pdfBytes == null || pdfBytes.length == 0) {\n    throw new IllegalArgumentException(\"Cannot upload empty PDF\");\n}\n// Check PDF signature\nif (pdfBytes.length < 5 || pdfBytes[0] != '%' || pdfBytes[1] != 'P') {\n    throw new IllegalArgumentException(\"File does not start with PDF signature\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    fileId = client.uploadFile(pdfBytes);\n} catch (IOException e) {\n    String msg = e.getMessage();\n    if (msg.contains(\"status 429\") || msg.contains(\"status 5\")) {\n        // Transient — retry with backoff\n        Thread.sleep(2000);\n        fileId = client.uploadFile(pdfBytes);\n    } else if (msg.contains(\"status 401\") || msg.contains(\"status 403\")) {\n        // Auth failure — do not retry, fix credentials\n        throw new SecurityException(\"Authentication failed for Hancom upload\", e);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Implement exponential backoff for 429 and 5xx responses.","Do not retry on 401/403 — fix credentials instead.","Check the response body for Hancom's error code (codeNum/code) to classify the failure.","Log the HTTP status and body for every upload failure for diagnostics."],"tags":["network","http","hancom","upload","hybrid"],"backgroundTag":null,"analyzedSha":"a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8","analyzedAt":"2026-08-14T05:22:03.953Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}