{"record":{"id":"5f0da577b38a9342","repo":"opendataloader-project/opendataloader-pdf","slug":"empty-response-body-from-visualinfo","errorCode":null,"errorMessage":"Empty response body from visualinfo","messagePattern":"Empty response body from visualinfo","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HancomClient.java","lineNumber":243,"sourceCode":"            \"?engine=\" + ENGINE +\n            \"&dlaMode=\" + DLA_MODE +\n            \"&ocrMode=\" + OCR_MODE;\n\n        Request request = new Request.Builder()\n            .url(url)\n            .get()\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 visualinfo 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 visualinfo\");\n            }\n\n            return objectMapper.readTree(body.string());\n        }\n    }\n\n    /**\n     * Deletes an uploaded file from the server.\n     *\n     * <p>This method silently ignores any errors to ensure cleanup\n     * doesn't interfere with the main processing result.\n     *\n     * @param fileId The file ID to delete.\n     */\n    private void deleteFile(String fileId) {\n        String url = baseUrl + String.format(DELETE_ENDPOINT, fileId);\n\n        Request request = new Request.Builder()","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/opendataloader-project/opendataloader-pdf/blob/a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8/java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/HancomClient.java#L225-L261","documentation":"The visualinfo endpoint returned a successful (2xx) HTTP status but response.body() is null. This mirrors error 64 (same condition on the upload endpoint) — the server claims success but provides no JSON body to parse for visual info data. This is a transport-level anomaly, not a business-logic error.","triggerScenarios":"Calling HancomClient.getVisualInfo(fileId) when the server returns a 200/204 with no entity body, or the response stream is prematurely closed at the network layer. Unlike error 67 (non-2xx), the HTTP status indicates success but the body is absent.","commonSituations":"Reverse proxy (nginx, HAProxy) buffering or stripping response bodies for large JSON payloads; server-side timeout during visual info serialization causes the handler to return before writing the body; HTTP/2 stream reset after headers; intermediate caching layer serving a stale empty response.","solutions":["Retry the getVisualInfo call — this is typically a transient transport issue.","Check proxy configurations for response body size limits or buffering settings.","Test the endpoint directly with curl bypassing any proxy: `curl -s '<visualinfo-url>?fileId=...'`.","Add an OkHttp logging interceptor to capture response headers (Content-Length, Transfer-Encoding) for diagnosis.","If persistent, escalate to Hancom support — a 2xx with empty body violates the API contract."],"exampleFix":"// No client-side fix — this is a server/proxy anomaly.\n// Best practice: retry once and log response headers.\nResponse response = httpClient.newCall(request).execute();\nif (response.isSuccessful() && response.body() == null) {\n    LOGGER.severe(\"Visualinfo succeeded (HTTP \" + response.code() + \") but body is null.\");\n    throw new IOException(\"Empty response body from visualinfo\");\n}","handlingStrategy":"retry","validationCode":"// Cannot pre-validate server response body existence.\n// Enable OkHttp connection failure retry:\nOkHttpClient client = new OkHttpClient.Builder()\n    .retryOnConnectionFailure(true)\n    .build();","typeGuard":null,"tryCatchPattern":"try {\n    JsonNode visualInfo = client.getVisualInfo(fileId);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"Empty response body\")) {\n        // Transient — retry once\n        visualInfo = client.getVisualInfo(fileId);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Enable OkHttp retryOnConnectionFailure(true) for transient transport issues.","Log response headers when body is null to identify proxy/server anomalies.","Test the visualinfo endpoint directly with curl to bypass proxies."],"tags":["network","http","hancom","visualinfo","hybrid","protocol-error"],"backgroundTag":null,"analyzedSha":"a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8","analyzedAt":"2026-08-14T05:22:03.953Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}