{"record":{"id":"117cb529b99eda13","repo":"PaddlePaddle/PaddleOCR","slug":"service-unavailable-msg","errorCode":null,"errorMessage":"Service unavailable: {msg}","messagePattern":"Service unavailable: (.+?)","errorType":"exception","errorClass":"ServiceUnavailableError","httpStatus":503,"severity":"warning","filePath":"paddleocr/_api_client/_core.py","lineNumber":158,"sourceCode":"        job_id=job_id,\n        state=state,\n        progress=progress,\n        result=data.get(\"resultUrl\"),\n        error_msg=data.get(\"errorMsg\"),\n    )\n\n\ndef raise_for_status(status_code: int, msg: str) -> None:\n    if 200 <= status_code < 300:\n        return\n    if status_code in (401, 403):\n        raise AuthError(f\"Authentication failed: {msg}\")\n    if status_code == 400:\n        raise InvalidRequestError(f\"Bad request: {msg}\")\n    if status_code == 429:\n        raise RateLimitError(f\"Rate limit exceeded: {msg}\")\n    if status_code in (503, 504):\n        raise ServiceUnavailableError(status_code, f\"Service unavailable: {msg}\")\n    raise APIError(status_code, msg)\n\n\ndef unwrap_api_response(payload: dict, status_code: int) -> dict:\n    if not isinstance(payload, dict):\n        raise ResponseFormatError(\"Response body must be a JSON object.\")\n    code = payload.get(\"code\", 0)\n    if code not in (0, None):\n        raise APIError(status_code, extract_api_message_from_payload(payload) or \"\")\n    data = payload.get(\"data\")\n    if not isinstance(data, dict):\n        raise ResponseFormatError(\"Response JSON must contain object field 'data'.\")\n    return data\n\n\ndef extract_job_id(data: dict) -> str:\n    job_id = data.get(\"jobId\")\n    if not isinstance(job_id, str) or not job_id:","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr/_api_client/_core.py#L140-L176","documentation":"Raised as ServiceUnavailableError by raise_for_status() when the HTTP response status is 503 or 504. The exception carries the status code plus the message. It indicates a server-side or gateway problem (overload, maintenance, upstream timeout), typically transient and worth retrying with backoff.","triggerScenarios":"Any API call during service maintenance or overload returning 503, or a long-running upstream operation timing out at the gateway returning 504.","commonSituations":"Service deployments/maintenance windows, traffic spikes, or intermittent gateway timeouts on large file uploads.","solutions":["Retry the same request after a short backoff (seconds, then exponential), ideally 3-5 attempts.","Check the service status page for ongoing incidents.","Reduce request size (smaller files) if 504s correlate with large uploads.","If persistent, report with timestamps to the service operator."],"exampleFix":"# before\nresult = await client.ocr(file_path=\"big.pdf\")\n\n# after\nimport asyncio\nfor attempt in range(5):\n    try:\n        result = await client.ocr(file_path=\"big.pdf\")\n        break\n    except ServiceUnavailableError:\n        await asyncio.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"from paddleocr._api_client.errors import ServiceUnavailableError\nimport asyncio\n\nasync def with_backoff(fn, *a, attempts=5, **kw):\n    for i in range(attempts):\n        try:\n            return await fn(*a, **kw)\n        except ServiceUnavailableError:\n            if i == attempts - 1:\n                raise\n            await asyncio.sleep(2 ** i)","preventionTips":["Retry 503/504 with exponential backoff; they are transient by nature.","Check the service status page before debugging your own code.","Alert only after several consecutive failures."],"tags":["http","availability","retryable","server-side"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}