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

  1. Print or log the chained JSONDecodeError (e.original) and fetch the raw result URL with curl to inspect the actual body being returned
  2. If a proxy/CDN is intercepting the URL, whitelist the result domain or bypass the proxy for it
  3. 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
  4. 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

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

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/71d1912543ea6a9d. Report an issue: GitHub.