PaddlePaddle/PaddleOCR · error · ResultParseError
Malformed JSONL result payload: {e}
Error message
Malformed JSONL result payload: {e} What it means
Raised as ResultParseError when the JSONL payload downloaded from the OCR job's result URL cannot be parsed. fetch_jsonl splits the response body into lines and calls json.loads on each non-empty line; any line that is not valid JSON triggers this error with the underlying JSONDecodeError chained as the cause.
Source
Thrown at paddleocr/_api_client/_http.py:203
def fetch_jsonl(self, url: str) -> list:
# Result URLs are often pre-signed object storage links.
try:
resp = requests.get(url, timeout=self._timeout)
except requests.Timeout as e:
raise RequestTimeoutError(f"Request timed out: {e}") from e
except requests.ConnectionError as e:
raise NetworkError(f"Connection failed: {e}") from e
try:
resp.raise_for_status()
lines = resp.text.strip().split("\n")
results = []
for line in lines:
line = line.strip()
if line:
results.append(json.loads(line))
return results
except json.JSONDecodeError as e:
raise ResultParseError(f"Malformed JSONL result payload: {e}") from e
def close(self):
self._session.close()
View on GitHub (pinned to 2661c7c0ef)
Solutions
- Print or log the chained JSONDecodeError (e.original) and fetch the raw result URL with curl to inspect the actual body being returned
- If a proxy/CDN is intercepting the URL, whitelist the result domain or bypass the proxy for it
- If the body is a single JSON object rather than JSONL, your PaddleOCR server version may emit a different format — pin/align the client version with your server version
- If parsing legitimately varies, download resp.text yourself and parse defensively instead of relying on fetch_jsonl
Example fix
# before jsonl_data = client._http.fetch_jsonl(json_url) # raises ResultParseError on bad body # after import requests, json resp = requests.get(json_url, timeout=300) resp.raise_for_status() jsonl_data = [json.loads(l) for l in resp.text.splitlines() if l.strip()] # inspect/parse manually
Defensive patterns
Strategy: try-catch
Validate before calling
import json
def valid_jsonl(text: str) -> bool:
return all(
json.loads(line) is not None
for line in text.splitlines() if line.strip()
) Type guard
def is_ocr_line(obj) -> bool:
return isinstance(obj, dict) and isinstance(obj.get('result'), dict) Try / catch
from paddleocr._api_client.errors import ResultParseError
try:
results = http.fetch_jsonl(json_url)
except ResultParseError as e:
logger.error("bad payload from %s: %s", json_url, e)
raise Prevention
- Log the raw response body the first time parsing fails so schema problems are diagnosable
- Pin client and server versions together so the JSONL schema cannot drift
- Route result downloads through the same network path used for API calls to avoid proxy interference
When it happens
Trigger: Calling the high-level OCR API (create job -> poll until done -> fetch results): the job reaches state 'done', the client downloads the JSONL from the result URL, but the body contains an HTML error page, a proxy login page, a truncated response, or non-JSON content on any single line.
Common situations: Result URL served through a corporate proxy or CDN that injects an error page; expired/mis-signed result URL returning a 200 with an error body; response truncated by a flaky connection; server-side format change where the endpoint starts returning a single JSON object with embedded newlines inside strings.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse JSONL result payload.
- Malformed JSONL result payload: {e}
- Malformed OCR result payload: {e}
- {file_path}
- Response body is not valid JSON: {e}
AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14).
Data as JSON: /api/errors/71d1912543ea6a9d.
Report an issue: GitHub.