Celery: Received unregistered task of type (KeyError) (tested)
Received unregistered task of type 'x' with KeyError: 'x' means the worker never imported the module that defines task x. Add it with Celery(..., include=["myapp.tasks"]) or app.autodiscover_tasks(), restart the worker, and check that it appears under [tasks] in the startup banner.
A Celery worker only knows the tasks whose modules it has imported. When a message arrives for a name that is not in its registry, the worker logs Received unregistered task of type ... with a KeyError traceback and discards the message. Measured with Celery 5.6.3 and Redis 7.
1. Reproduce: the task module is never imported by the worker
$ cat celery_app.py
from celery import Celery
app = Celery("proj", broker="redis://127.0.0.1:8964/0", backend="redis://127.0.0.1:8964/1")
# tasks live in myapp/tasks.py, but nothing imports that module in the worker
$ cat myapp/tasks.py
from celery_app import app
@app.task
def add(x, y):
return x + y
$ cat send.py
from celery_app import app
from celery.exceptions import NotRegistered
r = app.send_task("myapp.tasks.add", args=(2, 3))
try:
print("result:", r.get(timeout=5))
except Exception as e:
print("caller:", type(e).__name__, e)
try:
app.tasks["myapp.tasks.add"]
except NotRegistered as e:
print("app.tasks lookup:", type(e).__name__, e)
$ celery -A celery_app worker -c 1 --loglevel=INFO > worker.log 2>&1 &
$ timeout 30 python send.py
caller: NotRegistered 'myapp.tasks.add'
app.tasks lookup: NotRegistered 'myapp.tasks.add'
$ grep -A1 "\[tasks\]" worker.log; grep -E "unregistered|ignored and discarded|^Did you|^KeyError" worker.log | sed -E "s/^\[[^]]*\] //"
[tasks]
Received unregistered task of type 'myapp.tasks.add'.
The message has been ignored and discarded.
Did you remember to import the module containing this task?
KeyError: 'myapp.tasks.add'
The [tasks] section of the worker banner is empty, so the worker has no myapp.tasks.add. The caller got NotRegistered back from the result backend, and the message was not retried: it is gone.
2. Fix: make the worker import the module
$ cat celery_app.py
from celery import Celery
app = Celery("proj", broker="redis://127.0.0.1:8964/0", backend="redis://127.0.0.1:8964/1",
include=["myapp.tasks"]) # the worker imports this module at startup
$ celery -A celery_app worker -c 1 --loglevel=INFO > worker.log 2>&1 &
$ timeout 30 python send.py
result: 5
app.tasks lookup: NotRegistered 'myapp.tasks.add'
$ grep -A1 "\[tasks\]" worker.log; grep -c unregistered worker.log
[tasks]
. myapp.tasks.add
0
With include=["myapp.tasks"] the banner lists . myapp.tasks.add and the same call returns 5. Alternatives: app.autodiscover_tasks(["myapp"]) (looks for myapp/tasks.py; in Django, app.autodiscover_tasks() scans INSTALLED_APPS), or imports= in the config. Note include is applied when the worker starts; the client process above still had no add in app.tasks, which is fine for send_task by name.
Other causes when the module is imported
- The name differs: compare the name in the error with the
[tasks]list. A module imported astasksin one process andmyapp.tasksin another registers different names; set@app.task(name="myapp.tasks.add")or use the same import path everywhere. - An old worker: you added the task but did not restart the worker, or a second worker (another machine or container) consumes the same queue.
Related: Celery Error 111 connection refused, Celery chord error handling, Celery WorkerLostError.
Frequently asked questions
What does Celery 'Received unregistered task of type' mean?
The worker got a message for a task name that is not in its registry, because the module defining the task was never imported in the worker process.
Is the task retried after an unregistered task KeyError?
No. In our test the worker discarded the message and the caller received NotRegistered.
How do I see which tasks a Celery worker has registered?
Start it with --loglevel=INFO and read the [tasks] list in the banner, or run celery -A proj inspect registered.
Written with AI assistance. Measured on 2026-10-23 with Celery 5.6.3 (kombu 5.6.2, redis-py 8.1.0) on Python 3.13.5, Redis 7 (Docker) as broker and backend. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Celery and Redis are free.