Queues, Retries, and Idempotency for Reliable AI Jobs
How to survive duplicate clicks, worker crashes, provider timeouts, output-copy failures, billing uncertainty, and at-least-once delivery.
In this article

The Short Answer
Expensive AI work must assume that requests time out, workers disappear, networks split, and users click twice. Reliability comes from giving every user intent a stable identity, recording every attempt, and making side effects safe to reconcile.
The most dangerous moment
One Logical Job Can Have Several Attempts
A logical job represents what the user asked to create. An attempt represents one execution on one worker or provider. Keep those identifiers separate.
job_456
intent: render scene 7, take 2
state: running
idempotency_key: create-scene-7-take-2
attempt_1
server: sage3
provider_prompt_id: abc
state: timed_out
attempt_2
server: sage2
state: runningThis distinction answers important questions. Did the user create one job or two? Which server performed the work? Has cost already been reserved? Does a provider prompt ID exist that can be checked before retrying?
Idempotency Protects The Create Boundary
An idempotency key names one mutation. The client sends the same key when retrying the same request. The server either returns the original result or rejects mismatched parameters instead of creating another job.
Stripe’s API is a useful public reference: it stores the first result for an idempotency key and returns that result for matching retries. It also compares parameters to prevent accidental reuse. The same principle applies to generation creation even when payments are not involved.
Keys should not contain sensitive prompts or personal data. A random identifier or stable client operation ID is enough. Scope keys to the authenticated owner so two users cannot collide.
Retries Need A Failure Policy
| Failure | Typical policy | Reason |
|---|---|---|
| Invalid dimensions | Do not retry | Permanent until input changes |
| Queue unavailable | Bounded backoff | Likely transient infrastructure failure |
| GPU OOM | Retry with changed placement | Same server may fail again |
| Provider timeout after submit | Reconcile first | Original work may still be running |
| Output copy failure | Retry copy | Do not regenerate completed pixels |
Backoff reduces synchronized retry storms. A common shape doubles the delay and adds jitter:
Leases And Heartbeats Detect Lost Workers
A queue handing a job to a worker does not prove the worker will finish. A lease marks temporary ownership. The worker renews it with heartbeats. If the lease expires, a recovery process can inspect provider state and decide whether to requeue.
Lease expiry is evidence that the worker stopped reporting, not evidence that external work stopped. ComfyUI or a cloud provider may still be generating. Store the provider prompt ID before waiting so recovery can query history.
Exactly-Once Execution Is The Wrong Goal
A worker can complete generation and crash before acknowledging the queue. The queue may then deliver the job again. Preventing every duplicate execution across all failures is difficult. Preventing duplicate user-visible effects is tractable.
- Use a unique constraint on the idempotency scope and key.
- Use conditional state transitions, such as running to completed only once.
- Store output identity and hash before publishing completion.
- Make cost settlement compare-and-set or transactionally guarded.
- Deduplicate progress events by job, attempt, and sequence number.
- Reconcile provider history before launching another expensive attempt.
Billing And Output Ownership Must Follow State
Reserve cost before execution if overspending must be prevented. Settle actual usage only once. Release or refund reservations on terminal failure according to a documented policy.
Outputs need the same discipline. A completed provider file should be copied into durable application storage and attached to the logical job. A copy retry should target that file. Regenerating creates a new stochastic result and may charge twice.
A Minimal State Machine
queued -> running -> completed
| |
| +-> retry_wait -> queued
| +-> reconciling -> running | completed | failed
+-> cancelled
+-> failed
Rules:
- terminal transitions are conditional
- attempts are append-only records
- completion owns one durable output
- settlement happens onceState changes should be logged with safe identifiers and reasons. The log should explain why a retry happened without leaking the user’s private prompt or media URL.
Frequently Asked Questions
What is the difference between a retry and a duplicate job?
A retry is another attempt to complete the same logical job. A duplicate is a second logical job created from the same user intent. Idempotency keys prevent accidental duplicates; attempt records describe retries.
Can a queue guarantee exactly-once execution?
Distributed queues normally provide at-least-once or at-most-once delivery properties. Exactly-once user-visible effects are built with idempotent operations, durable state, deduplication, leases, and reconciliation.
When should an AI generation be retried automatically?
Retry transient failures with bounded backoff when the previous attempt is known not to have completed. Validate permanent input errors immediately and reconcile indeterminate provider timeouts before generating again.
Sources
Primary queue and API reliability documentation used for the retry and idempotency model.
- Stripe idempotent requests defines key reuse, stored responses, parameter checks, and retention behavior.
- Stripe advanced error handling distinguishes content, network, and server failures and explains reconciliation risk.
- RQ jobs documents job records, statuses, results, and automatic retries.
- RQ exceptions and retries documents retry configuration and failed-job handling.
Keep reading
Related articles

From Click to Clip: Inside an AI Generation Job
Follow an AI generation request through validation, durable job state, Redis-backed queues, ComfyUI execution, WebSocket progress, output storage, and failure recovery.

How Live AI Progress Reaches the Browser
Trace GPU progress events through workers and APIs to the browser, compare WebSockets, SSE, and polling, and design secure reconnect and fallback behavior.