JobResult
Handle for an enqueued job — status polling, result retrieval, dependencies.
Handle for an enqueued job — status polling, result retrieval, dependencies.
Handle to an enqueued job. Provides methods to check status and retrieve results, both synchronously and asynchronously.
Returned by task.delay(),
task.apply_async(),
queue.enqueue(), and canvas operations.
job.idjob.id -> strThe unique job ID (UUIDv7, time-ordered).
job.statusjob.status -> strCurrent job status. Fetches fresh from the database on every access.
Returns one of: "pending", "running", "complete", "failed", "dead",
"cancelled".
job = add.delay(2, 3)
print(job.status) # "pending"
# ... after worker processes it ...
print(job.status) # "complete"A note on naming. The per-job status is
"complete"(singular). The aggregate count bucket fromqueue.stats()is"completed"(past tense). Status filters such aslist_jobs(status=…)accept either spelling.
job.progressjob.progress -> int | NoneCurrent progress (0–100) if reported by the task via
current_job.update_progress(). Returns None
if no progress has been reported. Refreshes from the database.
job.errorjob.error -> str | NoneError message if the job failed. None if the job hasn't failed. Refreshes
from the database.
job.errorsjob.errors -> list[dict]Full error history for this job — one entry per failed attempt. Each dict contains:
| Key | Type | Description |
|---|---|---|
id | str | Error record ID |
job_id | str | Parent job ID |
attempt | int | Retry attempt number |
error | str | Error message/traceback |
failed_at | str | ISO timestamp of the failure |
job = flaky_task.delay()
# ... after retries ...
for err in job.errors:
print(f"Attempt {err['attempt']}: {err['error']}")job.dependenciesjob.dependencies -> list[str]List of job IDs this job depends on. Returns an empty list if the job has no dependencies. See Dependencies.
job.dependentsjob.dependents -> list[str]List of job IDs that depend on this job. Returns an empty list if no other jobs depend on this one.
Both are direct edges only, one hop in each direction, and both query
storage on every read rather than caching — a property in a loop is a query in a
loop. Declared as get_dependencies and get_dependents; for the transitive
picture, queue.job_dag(job_id) walks both directions at once.
Edges never cross namespaces — an enqueue that would create one is rejected — so the lists are scoped by the anchor job. A job in another namespace reports no edges, exactly like an unknown id.
job.to_dict()job.to_dict() -> dictReturn all job fields as a plain dictionary. Useful for JSON serialization (e.g. in the dashboard or FastAPI integration). Refreshes from storage first, so the snapshot is current rather than whatever this handle last saw.
It deliberately omits the stored result: that is the encoded blob the declared
result_bytes carries, and JSON-serializing it would be lossy or invalid
depending on the serializer. Use result(), which decodes it. payload_bytes
is the same story on the way in — the encoded arguments, before the serializer
has turned them back into Python values. Both are PyJob properties; reach for
them only when you need the wire bytes, such as re-encoding a payload under a
different serializer.
job.result()job.result(
timeout: float = 30.0,
poll_interval: float = 0.05,
max_poll_interval: float = 0.5,
) -> AnyBlock until the job completes and return the deserialized result. Uses
exponential backoff for polling — starts at poll_interval and gradually
increases to max_poll_interval.
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout | float | 30.0 | Maximum seconds to wait |
poll_interval | float | 0.05 | Initial seconds between status checks |
max_poll_interval | float | 0.5 | Maximum seconds between status checks |
Raises:
TimeoutError — if the job doesn't complete within timeoutTaskFailedError — if the job status is "failed"MaxRetriesExceededError — if the job status is "dead" (all retries exhausted)TaskCancelledError — if the job status is "cancelled"SerializationError — if result deserialization failsfrom flexiq import TaskFailedError, MaxRetriesExceededError, TaskCancelledError
job = add.delay(2, 3)
result = job.result(timeout=10) # blocks, returns 5
# Handle specific failure modes
try:
result = job.result(timeout=10)
except TaskCancelledError:
print("Job was cancelled")
except MaxRetriesExceededError:
print("Job exhausted all retries")
except TaskFailedError:
print("Job failed")await job.aresult()await job.aresult(
timeout: float = 30.0,
poll_interval: float = 0.05,
max_poll_interval: float = 0.5,
) -> AnyAsync version of result(). Uses asyncio.sleep() instead of time.sleep(),
so it won't block the event loop.
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout | float | 30.0 | Maximum seconds to wait |
poll_interval | float | 0.05 | Initial seconds between status checks |
max_poll_interval | float | 0.5 | Maximum seconds between status checks |
Raises: same as result().
job = add.delay(2, 3)
result = await job.aresult(timeout=10)job.stream()job.stream(
timeout: float = 60.0,
poll_interval: float = 0.5,
) -> Iterator[Any]Iterate over partial results published by the task via
current_job.publish(). Yields each result as
it arrives, stops when the job reaches a terminal state.
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout | float | 60.0 | Maximum seconds to wait |
poll_interval | float | 0.5 | Seconds between polls |
job = batch_process.delay(items)
for partial in job.stream(timeout=120):
print(f"Got: {partial}")await job.astream()async for partial in job.astream(
timeout: float = 60.0,
poll_interval: float = 0.5,
) -> AsyncIterator[Any]Async version of stream(). Uses asyncio.sleep so it won't block the event
loop.
async for partial in job.astream(timeout=120):
print(f"Got: {partial}")