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
- Collect job results promptly after completion instead of long-delayed polling.
- Re-submit the conversion to regenerate the result if it has expired.
- If you control the deployment, increase the service's result retention/TTL configuration.
- 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
- Fetch results immediately after terminal status.
- Persist converted output to your own storage.
- Size the service result TTL to your workload cadence.
- Treat expired results as cache misses, not errors.
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
- Task {task_id} was not found.
- Timed out waiting for task {task_id} after {timeout:.2f}s.
- {last_status.error_message or f"Task {task_id} failed."}
- Result for task {task_id} is not ready.
- {task_failure.failure.message}
AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14).
Data as JSON: /api/errors/510b20ec46f77b32.
Report an issue: GitHub.