Celery WorkerLostError: Worker exited prematurely (tested)

WorkerLostError: Worker exited prematurely: signal 9 (SIGKILL) means the pool process running the task was killed from outside, usually by the out-of-memory killer. exitcode N means code in the task process called exit. Neither can be caught inside the task.

WorkerLostError: Worker exited prematurely means the prefork child process running your task died before it could report a result. Python code did not raise it: the process itself went away, so try/except inside the task cannot catch it. The text after "exited prematurely" tells you how it died.

1. exitcode vs signal 9 (SIGKILL)

$ cat tasks.py
import os, signal
from celery import Celery
app = Celery("tasks", broker="redis://127.0.0.1:8964/0", backend="redis://127.0.0.1:8964/1")

@app.task
def crash():
    os._exit(1)                            # like a C extension calling exit()

@app.task
def killed():
    os.kill(os.getpid(), signal.SIGKILL)   # what the Linux OOM killer does
$ celery -A tasks worker -c 1 --loglevel=INFO > worker.log 2>&1 &
$ cat run.py
from tasks import crash, killed
for t in (crash, killed):
    try:
        print(t.name, "->", t.delay().get(timeout=20))
    except Exception as e:
        print(t.name, "->", type(e).__name__, e)
$ timeout 60 python run.py
tasks.crash -> WorkerLostError Worker exited prematurely: exitcode 1 Job: 0.
tasks.killed -> WorkerLostError Worker exited prematurely: signal 9 (SIGKILL) Job: 1.

The worker log names the dead process:

$ grep -E "ERROR|WARNING|succeeded" worker.log | cut -c1-200
[2026-10-23 10:59:52,141: ERROR/MainProcess] Process 'ForkPoolWorker-1' pid:3012915 exited with 'exitcode 1'
[2026-10-23 10:59:52,159: ERROR/MainProcess] Task handler raised error: WorkerLostError('Worker exited prematurely: exitcode 1 Job: 0.')
[2026-10-23 10:59:52,185: ERROR/MainProcess] Process 'ForkPoolWorker-2' pid:3012960 exited with 'signal 9 (SIGKILL)'
[2026-10-23 10:59:52,197: ERROR/MainProcess] Task handler raised error: WorkerLostError('Worker exited prematurely: signal 9 (SIGKILL) Job: 1.')

2. SIGKILL from a hard time limit is reported as TimeLimitExceeded

$ grep -E "ERROR|WARNING|succeeded" worker.log | cut -c1-200
[2026-10-23 11:00:02,602: ERROR/MainProcess] Task handler raised error: TimeLimitExceeded(2)
[2026-10-23 11:00:02,602: ERROR/MainProcess] Hard time limit (2s) exceeded for tasks.slow[9516fbc7-4263-400d-82aa-873f4c047dee]
[2026-10-23 11:00:02,709: ERROR/MainProcess] Process 'ForkPoolWorker-1' pid:3012968 exited with 'signal 9 (SIGKILL)'
[2026-10-23 11:00:03,783: WARNING/MainProcess] Soft time limit (1s) exceeded for tasks.slow_soft[e188cdea-0f12-4ba6-a058-dae7eb169b79]
[2026-10-23 11:00:03,792: INFO/ForkPoolWorker-2] Task tasks.slow_soft[e188cdea-0f12-4ba6-a058-dae7eb169b79] succeeded in 1.0103442892432213s: 'cleaned up'

When Celery itself kills the child for exceeding time_limit, the log also shows exited with 'signal 9 (SIGKILL)', but the task result is TimeLimitExceeded, not WorkerLostError. A WorkerLostError with SIGKILL and no "Hard time limit" line above it points to something outside Celery, usually memory. See Celery task timeout / time limits.

What to do

Related: Celery chord error handling, Celery MaxRetriesExceededError, Celery link_error errback.

Frequently asked questions

What causes Celery WorkerLostError signal 9?

The prefork child was SIGKILLed from outside Celery, most often by the Linux OOM killer or a container memory limit.

Can I catch WorkerLostError inside a Celery task?

No. The process running the task is gone; the error is recorded by the parent worker process.

Is a Celery hard time limit reported as WorkerLostError?

No. Celery logs the SIGKILL but the result is TimeLimitExceeded.

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.