docling-project/docling · error · ResultExpiredError

Result for task {task_id} has expired.

Error message

Result for task {task_id} has expired.

What it means

Raised as ResultExpiredError when the result endpoint answers 'Task result not found', the last status is terminal (but not failure), meaning the task completed yet its stored result artifact is no longer available. Unlike a not-ready state, retrying will not help: the service has discarded the result, typically due to its result-retention policy.

Source

Thrown at docling/service_client/client.py:673

        now = datetime.now(tz=retry_at.tzinfo or timezone.utc)
        return max(0.0, (retry_at - now).total_seconds())

    def _raise_for_result_404(
        self,
        task_id: str,
        response: httpx.Response,
        last_status: TaskStatusResponse | None,
    ) -> None:
        detail = self._http_error_detail(response)
        if detail == "Task not found.":
            raise TaskNotFoundError(f"Task {task_id} was not found.")
        if detail is not None and detail.startswith("Task result not found"):
            if last_status is not None and is_terminal_task_status(last_status):
                if last_status.task_status == "failure":
                    message = last_status.error_message or f"Task {task_id} failed."
                    raise TaskExecutionError(message, failure=last_status.failure)
                raise ResultExpiredError(f"Result for task {task_id} has expired.")
            raise ResultNotReadyError(f"Result for task {task_id} is not ready.")
        raise ServiceError(
            "Unexpected result lookup error.",
            status_code=response.status_code,
            detail=detail,
        )

    def _raise_if_task_failure_result(self, response: httpx.Response) -> None:
        content_type = response.headers.get("content-type", "")
        if "json" not in content_type.lower():
            return

        try:
            payload = response.json()
        except ValueError:
            return

        if not isinstance(payload, dict) or payload.get("kind") != "TaskFailureResult":

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Collect job results promptly after completion instead of long-delayed polling.
  2. Re-submit the conversion to regenerate the result if it has expired.
  3. If you control the deployment, increase the service's result retention/TTL configuration.
  4. Persist converted output yourself (e.g. to object storage) as soon as a result is obtained.

Example fix

# before
result = job.result()  # days later: ResultExpiredError

# after
from docling.service_client.exceptions import ResultExpiredError

try:
    result = job.result()
except ResultExpiredError:
    job = client.submit(source)  # regenerate
    result = job.result()
    save_to_storage(result)      # persist immediately
Defensive patterns

Strategy: fallback

Try / catch

from docling.service_client.exceptions import ResultExpiredError

try:
    result = job.result()
except ResultExpiredError:
    job = client.submit(source)      # regenerate the artifact
    result = job.result()
    persist(result)                   # keep your own copy

Prevention

When it happens

Trigger: Calling result retrieval for a task whose last TaskStatusResponse is a terminal success status while the service returns 404 detail starting with 'Task result not found' — i.e. the task finished but the artifact was expired/GC'd.

Common situations: Collecting results hours or days after completion when the service only retains artifacts briefly; restarting a service with ephemeral storage; queue-backed deployments where completed results are pruned aggressively.

Related errors


AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14). Data as JSON: /api/errors/510b20ec46f77b32. Report an issue: GitHub.