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.
- Set a backend:
CELERY_RESULT_BACKEND = "redis://redis:6379/1"or"django-db"with django-celery-results. - Make sure
celery.pycallsapp.config_from_object("django.conf:settings", namespace="CELERY"). - Use the
CELERY_prefix on every Celery setting when using that namespace. - Restart both the web processes and the workers; both need the setting.
- If you never use return values, do not call
.get(); setignore_result=Trueon 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.
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.
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_EAGERruns tasks inline and returnsEagerResult, 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
- Set up broker and backend together with Configure Redis and Celery for Django in Production.
- If tasks stay in PENDING, see Fix Celery Tasks Stuck in PENDING.
- Review the rest with the Django Celery Production Checklist.
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.