fix(document): catch httpx timeout from gateway OCR backend (#892 r3)

Round-3 review on PR #892 found a real bug: the gateway backend's httpx.Timeout
raises httpx.ReadTimeout (a httpx.TimeoutException, NOT a builtin TimeoutError),
so the `except TimeoutError` added in r2 only covered the Mistral
(anyio.fail_after) path — gateway timeouts still fell through to
reason="error". Catch both (TimeoutError, httpx.TimeoutException) so either
backend's timeout lands in the dedicated parse_failed_reason="timeout" bucket.
Add an end-to-end test driving a gateway httpx.ReadTimeout through the
processor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-11 06:19:53 +02:00
co-authored by Claude Opus 4.8
parent 2f8875e736
commit ebd0b469f5
2 changed files with 26 additions and 5 deletions
@@ -264,11 +264,13 @@ class OcrProcessor(DocumentProcessor):
text, boundaries = await backend.ocr(
content, content_type.split(";")[0].strip().lower()
)
except TimeoutError:
# anyio.fail_after / httpx read-timeout raise TimeoutError with an
# empty message; give it its own reason bucket and a useful log so a
# too-low DOCUMENT_OCR_TIMEOUT_SECONDS is distinguishable from a
# provider that's actually erroring.
except (TimeoutError, httpx.TimeoutException):
# Two timeout shapes reach here: the Mistral backend's
# anyio.fail_after raises the builtin TimeoutError, while the gateway
# backend's httpx.Timeout raises httpx.ReadTimeout (a
# httpx.TimeoutException, NOT a TimeoutError). Catch both so a
# too-low DOCUMENT_OCR_TIMEOUT_SECONDS lands in its own reason bucket
# rather than being conflated with provider errors.
timeout = settings.document_ocr_timeout_seconds
logger.warning(
"OCR timed out for %s after %.1fs", filename or "<bytes>", timeout