{"record":{"id":"76211f75c46e6e4d","repo":"zylon-ai/private-gpt","slug":"document-conversion-failed-e","errorCode":null,"errorMessage":"Document conversion failed: {e}","messagePattern":"Document conversion failed: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"private_gpt/components/readers/docling/docling_api_reader.py","lineNumber":170,"sourceCode":"        execute_transformations: bool = True,\n        notification: NotifyProtocol | None = None,\n        *args: Any,\n        **load_kwargs: Any,\n    ) -> AsyncIterator[BaseNode]:\n        \"\"\"Lazy load file data into LlamaIndex Documents.\"\"\"\n        logger.debug(\"Starting Docling API parsing of file: %s\", file_info.file_name)\n\n        file_name = file_info.file_name or file_info.file_data.name\n        file_data = file_info.file_data\n        file_bytes = await asyncio.to_thread(file_data.read_bytes)\n        pages = file_info.config.get(\"pages\", None)\n\n        try:\n            conversion_result = await self.client.convert_from_bytes(\n                file_name, file_bytes, to_formats=[\"md\"], pages=pages, **load_kwargs\n            )\n        except Exception as e:\n            raise ValueError(f\"Document conversion failed: {e}\") from e\n\n        if conversion_result.status not in [\"success\", \"partial_success\"]:\n            raise ValueError(\n                f\"Document conversion failed with status: {conversion_result.status}. \"\n                f\"Errors: {conversion_result.errors}\"\n            )\n\n        contents = self._get_content(conversion_result)\n        valid_contents = [content for content in contents if content]\n        if not valid_contents:\n            raise ValueError(\"No valid document content found after conversion\")\n        if self._is_extraction_unsuccessful(valid_contents):\n            raise ExtractionUnsuccessfulError(\n                f\"Document extraction unsuccessful for '{file_name}': unmapped-glyph \"\n                f\"ratio exceeded threshold ({self.config.failure_threshold}).\"\n            )\n\n        docs = [","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/readers/docling/docling_api_reader.py#L152-L188","documentation":"Raised by DoclingApiReader.lazy_load_data when the underlying client.convert_from_bytes call throws any exception — the reader wraps it in ValueError with 'Document conversion failed: {original}' and chains the cause. The original exception can be an aiohttp error that already survived the client's retry decorators (5 tries with jitter on connection errors and timeouts), an HTTP 4xx/5xx from raise_for_status, or a payload/model error.","triggerScenarios":"Calling the docling reader on a file when: the Docling server is unreachable after retries (ClientConnectorError), the server returns 4xx/5xx on POST /convert/source (bad options, auth failure, payload too large), the request exceeds timeouts, or the response JSON does not match DoclingApiOutputModel (pydantic ValidationError).","commonSituations":"Docling server not running or wrong api_base in settings; api_key/tenant headers rejected (401/403); Docling server version that rejects v1alpha options; very large base64 payloads rejected by a proxy (413); network flaps exceeding the 5-retry budget.","solutions":["Read the chained cause (`e.__cause__`) — the fix depends entirely on the wrapped exception; for ClientConnectorError verify the server is up and docling.api_base is correct.","Curl the server directly: curl -X POST {api_base}/v1alpha/convert/source with a small file to confirm reachability and auth.","For HTTP errors, match the status: 401/403 → set docling.api_key/tenant_id; 413 → shrink file or raise proxy limits; 422 → options mismatch with server version.","For response-shape errors (ValidationError), check that docling.api_version ('v1alpha' vs 'v1') matches your Docling server release.","Catch this at the ingestion layer and quarantine the file rather than aborting the whole batch."],"exampleFix":"# before\ntry:\n    conversion_result = await self.client.convert_from_bytes(...)\nexcept Exception as e:\n    raise ValueError(f\"Document conversion failed: {e}\") from e\n\n# caller-side handling\ntry:\n    docs = await reader.lazy_load_data(file_info)\nexcept ValueError as e:\n    if str(e).startswith(\"Document conversion failed\") and e.__cause__:\n        logger.error(\"cause: %r\", e.__cause__)\n    raise","handlingStrategy":"try-catch","validationCode":"async def docling_reachable(api_base: str, api_key: str | None = None) -> bool:\n    import aiohttp\n    headers = {\"X-Api-Key\": api_key} if api_key else {}\n    try:\n        async with aiohttp.ClientSession() as s, s.get(f\"{api_base}\", headers=headers, timeout=aiohttp.ClientTimeout(total=5)) as r:\n            return r.status < 500\n    except aiohttp.ClientError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    nodes = [n async for n in reader.lazy_load_data(file_info)]\nexcept ValueError as e:\n    if str(e).startswith(\"Document conversion failed:\"):\n        cause = e.__cause__\n        if isinstance(cause, (aiohttp.ClientConnectorError, aiohttp.ServerDisconnectedError)):\n            schedule_retry(file_info)      # transient transport: retry later\n        else:\n            quarantine(file_info, repr(cause))  # likely file/options problem\n    else:\n        raise","preventionTips":["Health-check the Docling server before starting an ingestion batch.","Always inspect __cause__ — the wrapper message alone hides whether it is network, auth, or payload.","Match docling.api_version to your server generation ('v1alpha' for 0.x, 'v1' for newer)."],"tags":["docling","network","ingestion","error-wrapping"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}