01 / PUBLISH
Was the task actually sent?
Confirm that the call reached delay() or apply_async() and was not skipped by transaction rollback, conditional code or an earlier exception.
Django production problems / background jobs
Follow a task from Django to the broker, queue and worker. These seven checks separate “pending forever” from routing mistakes, blocked workers, retry loops and missing results.
For Django + Celery · Safe diagnostic order · No task arguments needed
Start with the symptom
Celery uses PENDING both for a task that may be waiting and for a task ID the result backend does not know. So checking AsyncResult.state alone cannot tell you where the work stopped.
Build the timeline instead: did Django publish the message, did the broker accept it, did a worker consume the correct queue, did execution start, and did a terminal state reach the result backend? The first missing transition points to the likely cause.
Primary references: Celery’s official documentation for task states, monitoring workers and events, and the official Django Tasks framework.
Seven checks, in order
Do not restart everything and hope. Find the first missing step between publish and completion, then investigate only that part of the chain.
01 / PUBLISH
Confirm that the call reached delay() or apply_async() and was not skipped by transaction rollback, conditional code or an earlier exception.
02 / WORKER
A running process may listen to another queue. Check active workers, their subscribed queues and whether every slot is occupied by long-running work.
03 / ROUTING
Compare the published task name with the worker’s registered tasks. Then check routing keys, queue names and imports after the latest deploy.
04 / RESULT
Verify the result backend and task ID. A missing or expired result also appears as PENDING, even when no message is waiting.
05 / EXECUTION
Inspect database locks, network calls without timeouts, large payloads and exhausted connection pools. A started task is a different problem from a queued task.
06 / RECOVERY
Inspect retry reason, backoff, maximum retries and soft or hard time limits. Repeated retries can look like a queue that never drains.
07 / SCHEDULE
Treat scheduling and execution separately. First prove Beat emitted the due task once; then repeat the broker, queue and worker checks above. Also verify timezone and duplicate scheduler instances.
Queued or already running?
A customer can wait ten minutes for a task that runs in two seconds. Looking only at execution time would call that healthy. Store publish and start timestamps so you can tell whether the queue or the task code caused the delay.
Segment by queue and task name before scaling workers. One overloaded low-priority queue can otherwise hide inside a harmless portfolio average.
Prevent the next mystery
FetchNode can observe Celery publish, start, success, failure and retry signals when the Django integration loads. That turns “the email never arrived” into a concrete queue, worker, retry or exception timeline.
from fetchnode_client import capture_job
@capture_job(job_name="billing.send_invoice", queue="billing")
def send_invoice(invoice_id):
invoice = Invoice.objects.get(pk=invoice_id)
deliver_invoice(invoice)
Keep the task name stable. Pass identifiers rather than personal data, and make retries idempotent before automating recovery.
After the immediate fix
Stable names and explicit queues make release comparisons and ownership possible.
Success, failure and retry must close the execution that started.
Use per-task expectations instead of one global timeout for every queue.
Trigger a controlled exception and confirm the alert links to useful context.
Detect silence from workers or the scheduler, not only reported exceptions.
Document when manual replay is safe and who checks idempotency first.
For the rest of the production stack, use the 12-point Django monitoring checklist or compare Django APM tools.
Celery troubleshooting FAQ
PENDING can mean waiting, but Celery also returns it when the result backend has no information for the task ID. Check publish and worker events before assuming the task is still queued.
Common causes are a worker consuming a different queue, an unregistered task name, a broker connection problem or all worker slots being occupied by long-running work.
Treat it as potentially stalled when it reported a start but no success, failure or retry within a threshold based on its normal duration. Confirm worker state before replaying it.
Beat only publishes scheduled tasks. Prove that publication happened, then check the broker, target queue, routing and worker as separate stages.
Only after checking that the original attempt is no longer running and that the task is idempotent. Otherwise a manual retry can duplicate emails, charges or other side effects.
Start with one production project, verify a successful task and a controlled failure, then detect jobs that fail, retry repeatedly or never report completion.
Choose whether to allow analytics
FetchNode uses optional analytics storage for pageviews and product journeys. Operational error monitoring remains separate. Read the privacy policy.