Fix Celery "No Result Backend Is Configured" in Django

Fix Celery errors No result backend is configured and DisabledBackend object has no attribute _get_task_meta_for in Django: configure CELERY_RESULT_BACKEND, load settings with the CELERY namespace, or stop calling .get() on tasks that do not need results.

Problem statement

Calling .get(), .status, or .ready() on a task result fails:

NotImplementedError: No result backend is configured.
Please see the documentation for more information.

or with older Celery versions:

AttributeError: 'DisabledBackend' object has no attribute '_get_task_meta_for'

Celery separates sending tasks (the broker) from storing their return values (the result backend). Without a result backend, tasks still run, but nothing records their state or result, so any code that asks for it fails. In Django projects, the cause is usually that CELERY_RESULT_BACKEND is not set, or that Celery is not reading Django settings with the namespace your settings use.

Why results are unavailableTask sentvia brokerWorker runs itresult discardedNo backendDisabledBackend.get() failsnot configuredThe broker carries tasks; results need their own backend.
  1. Set a backend: CELERY_RESULT_BACKEND = "redis://redis:6379/1" or "django-db" with django-celery-results.
  2. Make sure celery.py calls app.config_from_object("django.conf:settings", namespace="CELERY").
  3. Use the CELERY_ prefix on every Celery setting when using that namespace.
  4. Restart both the web processes and the workers; both need the setting.
  5. If you never use return values, do not call .get(); set ignore_result=True on fire-and-forget tasks.

Step-by-step solution

1. Check the effective configuration

python manage.py shell -c "from config.celery import app; print(app.conf.result_backend)"

None or disabled:// confirms no backend is configured in the process that runs this code. Run the same check in a worker container if the error happens there.

2. Configure a result backend

Redis is the common choice when it already serves as the broker:

CELERY_BROKER_URL = os.environ["REDIS_URL"] + "/0"
CELERY_RESULT_BACKEND = os.environ["REDIS_URL"] + "/1"
CELERY_RESULT_EXPIRES = 60 * 60 * 24   # one day

To store results in the Django database instead, install django-celery-results, add it to INSTALLED_APPS, run migrations, and set:

CELERY_RESULT_BACKEND = "django-db"
CELERY_RESULT_EXTENDED = True

The database backend lets you browse results in the admin, at the cost of writes to your main database for every task.

where task results can be storedGood forWatch out forRedisfast status checksset result_expiresdjango-dbadmin visibilitydatabase writesnonefire-and-forgetno .get() or statusRPC (AMQP)RabbitMQ setupsresults read oncePick a backend only if code actually reads resultsdjango-deployment.com
Many projects need no backend at all, as long as no code calls .get().

3. Fix the settings namespace

The standard celery.py:

import os
from celery import Celery

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
app = Celery("config")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

With namespace="CELERY", Celery reads only settings starting with CELERY_. A setting named RESULT_BACKEND or result_backend in Django settings is ignored. Conversely, without the namespace argument, CELERY_RESULT_BACKEND is ignored. Pick one style and use it consistently.

4. Restart every process

The web process that calls .get() and the worker that stores the result both need the backend. A worker started before the change keeps running with results disabled, and tasks stay PENDING forever from the web side. Restart both after changing settings.

5. Do not ask for results you do not need

Calling .get() inside a web request blocks the request until the task finishes, which defeats the point of a background task and can deadlock under load. For tasks like sending email, just call .delay() and move on:

@shared_task(ignore_result=True)
def send_welcome_email(user_id):
    ...

If you need to show progress, store state in your own model or cache key and poll that.

cause and fixWhat you findCause and fixresult_backend is Nonenot set; add CELERY_RESULT_BACKENDset as RESULT_BACKENDnamespace ignores it;add CELERY_ prefixweb has it, worker notstale worker; restart all processes.get() in a viewblocks request; poll or ignore resultConfiguration and process restarts fix most casesdjango-deployment.com
Check app.conf.result_backend in each process type to see what it actually loaded.

Explanation

When no result backend is configured, Celery uses DisabledBackend, a placeholder that raises whenever something tries to read or wait for a result. Tasks still execute; only their outcomes are discarded. This is a reasonable default, because storing results costs memory and storage, and most background tasks do not need them. The error appears when code assumes results exist, most often result.get() in a view, test, or management command.

Edge cases / notes

  • Tests. CELERY_TASK_ALWAYS_EAGER runs tasks inline and returns EagerResult, which works without a backend. Production behaves differently.
  • Chords. Celery chords require a result backend, because the callback waits for all header tasks' results.
  • Expiry. Without result_expires, Redis keeps results until memory pressure evicts them; set an expiry.

Next steps

FAQ

Do Celery tasks run without a result backend?

Yes. They run normally; only their state and return values are not stored.

Which result backend should I use?

Redis for speed when it is already your broker, django-celery-results if you want results in the admin, none for fire-and-forget tasks.

Why does my setting not take effect?

With namespace="CELERY", only settings prefixed with CELERY_ are read. Check the name and restart the workers.

Is calling .get() in a Django view a good idea?

Rarely. It blocks the request until the task finishes. Return immediately and poll for status instead.

Keep going