Django production problems / background jobs

Celery task stuck or not running? Find out why.

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

“Pending” does not always mean your task is waiting

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

Where did the Celery task stop?

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

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.

02 / WORKER

Is a worker consuming this queue?

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

Does the worker know the task?

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

Is PENDING only missing state?

Verify the result backend and task ID. A missing or expired result also appears as PENDING, even when no message is waiting.

05 / EXECUTION

Is the task blocked inside its work?

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

Is it retrying or timing out forever?

Inspect retry reason, backoff, maximum retries and soft or hard time limits. Repeated retries can look like a queue that never drains.

07 / SCHEDULE

If it is periodic, did Celery Beat publish it?

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?

The same “slow task” symptom has two different causes

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.

SignalLikely problemFirst check
High queue delayCapacity or routingActive workers, queue depth and prefetch
High durationTask or dependencyDatabase, network calls and payload size
Frequent retriesTransient dependencyRetry reason, backoff and idempotency
No terminal stateWorker loss or hard timeoutWorker logs, time limits and broker visibility

Prevent the next mystery

Record where every important task stops

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.

Django Tasks or a generic jobPython
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

Stop Celery tasks from failing silently again

  1. 1. Name and route tasks consistently

    Stable names and explicit queues make release comparisons and ownership possible.

  2. 2. Record every terminal state

    Success, failure and retry must close the execution that started.

  3. 3. Baseline delay and duration

    Use per-task expectations instead of one global timeout for every queue.

  4. 4. Test failure and retry paths

    Trigger a controlled exception and confirm the alert links to useful context.

  5. 5. Add a scheduled heartbeat

    Detect silence from workers or the scheduler, not only reported exceptions.

  6. 6. Define a retry owner

    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

Common reasons tasks stay pending or never run

Why is my Celery task stuck in PENDING?

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.

Why is my Celery worker not picking up tasks?

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.

How can I tell whether a Celery task is really stuck?

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.

Why does Celery Beat send tasks that never run?

Beat only publishes scheduled tasks. Prove that publication happened, then check the broker, target queue, routing and worker as separate stages.

Is it safe to retry a stuck Celery task?

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.

Know a background task failed before a customer reports it

Start with one production project, verify a successful task and a controlled failure, then detect jobs that fail, retry repeatedly or never report completion.