The Fix
pip install celery==5.6.0
Based on closed celery/celery issue #8144 · PR/commit linked
@@ -2234,7 +2234,8 @@ def run(self, header, body, partial_args, app=None, interval=None,
body.options.update(options)
- bodyres = body.freeze(task_id, root_id=root_id)
+ body_task_id = task_id or uuid()
+ bodyres = body.freeze(body_task_id, group_id=group_id, root_id=root_id)
from celery import Celery, group
app = Celery('repro_toy')
app.conf.update(
broker_url='redis://localhost/0',
result_backend='redis://localhost/0',
#task_serializer='pickle',
#result_serializer='pickle',
#accept_content=['application/json', 'application/x-python-serialize']
)
@app.task(bind=True)
def debug_A(self, i):
print(f'Doing A{i}')
@app.task(bind=True)
def debug_broke(self):
print(f'OH NO')
raise Exception("Head Aspload")
@app.task(bind=True)
def debug_finisher(self):
print(f'OK, We did it all')
def probe_results(res):
print (f"waiting for {res=}")
res.wait()
print(f"{res.failed()=}")
print(f"{res.get()=}")
def launch():
# This finishes fine
g = [debug_A.si(i) for i in range(5)]
r = group(g) | debug_finisher.si() | debug_A.si(-1)
res = r.apply_async()
probe_results(res)
def launch_fail_better():
# This blows up with a nice expected celery.exceptions.ChordError which seems correct
# When I get reults I get my "Head Aspload" Exception
g = [debug_A.si(i) for i in range(5)]
g.append(debug_broke.si())
r = group(g) | debug_finisher.si()
res = r.apply_async()
probe_results(res)
def launch_fail_bad():
# This blows up with TypeError: sequence item 1: expected a bytes-like object, NoneType found
g = [debug_A.si(i) for i in range(5)]
g.append(debug_broke.si())
r = group(g) | debug_finisher.si() | debug_A.si(-1)
res = r.apply_async()
probe_results(res)
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Upgrade to fixed release\npip install celery==5.6.0\nWhen NOT to use: This fix should not be applied if the task_id is intentionally meant to be None.\n\n
Why This Fix Works in Production
- Trigger: When a task fails in the body of a chord, and that chord chains into another task, there another buggy exception raised
- Mechanism: The chord body was being frozen with a potentially None task_id, causing error handling to fail
- Why the fix works: Fixes a bug where a ValueError was raised when a task_id was empty in a chord with a chain body. (first fixed release: 5.6.0).
Why This Breaks in Prod
- The chord body was being frozen with a potentially None task_id, causing error handling to fail
- Production symptom (often without a traceback): When a task fails in the body of a chord, and that chord chains into another task, there another buggy exception raised
Proof / Evidence
- GitHub issue: #8144
- Fix PR: https://github.com/celery/celery/pull/9774
- First fixed release: 5.6.0
- Reproduced locally: No (not executed)
- Last verified: 2026-02-09
- Confidence: 0.85
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.38
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“Just tried this: with latest version 5.5.3 the error on your is not TypeError: sequence item 1: expected a bytes-like object, NoneType found but ValueError:…”
“so I guess we can close this issue as well, right?”
“Hey @mscout1 :wave:, Thank you for opening an issue”
Failure Signature (Search String)
- When a task fails in the body of a chord, and that chord chains into another task, there another buggy exception raised
- If I leave off the chain, and just run the chord, It does not have this second exception. In that case, I get the original exception from the chord head task when I wait.
Copy-friendly signature
Failure Signature
-----------------
When a task fails in the body of a chord, and that chord chains into another task, there another buggy exception raised
If I leave off the chain, and just run the chord, It does not have this second exception. In that case, I get the original exception from the chord head task when I wait.
Error Message
Signature-only (no traceback captured)
Error Message
-------------
When a task fails in the body of a chord, and that chord chains into another task, there another buggy exception raised
If I leave off the chain, and just run the chord, It does not have this second exception. In that case, I get the original exception from the chord head task when I wait.
Minimal Reproduction
from celery import Celery, group
app = Celery('repro_toy')
app.conf.update(
broker_url='redis://localhost/0',
result_backend='redis://localhost/0',
#task_serializer='pickle',
#result_serializer='pickle',
#accept_content=['application/json', 'application/x-python-serialize']
)
@app.task(bind=True)
def debug_A(self, i):
print(f'Doing A{i}')
@app.task(bind=True)
def debug_broke(self):
print(f'OH NO')
raise Exception("Head Aspload")
@app.task(bind=True)
def debug_finisher(self):
print(f'OK, We did it all')
def probe_results(res):
print (f"waiting for {res=}")
res.wait()
print(f"{res.failed()=}")
print(f"{res.get()=}")
def launch():
# This finishes fine
g = [debug_A.si(i) for i in range(5)]
r = group(g) | debug_finisher.si() | debug_A.si(-1)
res = r.apply_async()
probe_results(res)
def launch_fail_better():
# This blows up with a nice expected celery.exceptions.ChordError which seems correct
# When I get reults I get my "Head Aspload" Exception
g = [debug_A.si(i) for i in range(5)]
g.append(debug_broke.si())
r = group(g) | debug_finisher.si()
res = r.apply_async()
probe_results(res)
def launch_fail_bad():
# This blows up with TypeError: sequence item 1: expected a bytes-like object, NoneType found
g = [debug_A.si(i) for i in range(5)]
g.append(debug_broke.si())
r = group(g) | debug_finisher.si() | debug_A.si(-1)
res = r.apply_async()
probe_results(res)
What Broke
Chaining tasks after a failed chord results in obscured error messages, leading to confusion.
Why It Broke
The chord body was being frozen with a potentially None task_id, causing error handling to fail
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
pip install celery==5.6.0
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Fix reference: https://github.com/celery/celery/pull/9774
First fixed release: 5.6.0
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix should not be applied if the task_id is intentionally meant to be None.
Verify Fix
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Did This Fix Work in Your Case?
Quick signal helps us prioritize which fixes to verify and improve.
Prevention
- Capture the exact failing error string in logs and tests so you can reproduce via a minimal script.
- Pin production dependencies and upgrade only with a reproducible test that hits the failing path.
Version Compatibility Table
| Version | Status |
|---|---|
| 5.6.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.