The Fix
pip install celery==5.4.0
Based on closed celery/celery issue #8678 · PR/commit linked
Production note: This usually shows up under retries/timeouts. Treat it as a side-effect risk until you can verify behavior with a canary + real traffic.
@@ -2271,6 +2271,8 @@ def link_error(self, errback):
applied to the body.
"""
+ errback = maybe_signature(errback)
+
if self.app.conf.task_allow_error_cb_on_chord_header:
from celery import Celery, chain, chord, signature, group
app = Celery(
"celery_bug", backend="redis://0.0.0.0:6479/", broker="redis://0.0.0.0:6479/"
)
app.conf.task_allow_error_cb_on_chord_header = True
@app.task(bind=True)
def orig_task(self, arg):
chord_headers = group([chord_header.s(arg=f"arg{i}") for i in range(5)])
replacement_chord = chord(chord_headers, chord_body.s())
return self.replace(replacement_chord)
@app.task
def chord_header(arg):
return f"header: {arg}"
@app.task
def chord_body(arg):
return f"body: {arg}]"
@app.task
def handle_error(*args, **kwargs):
print(f"handle error called with args {args} kwargs {kwargs}")
def main():
print(f"hello world")
res = orig_task.apply_async(args=["spam"], link_error=handle_error.s())
print(f"RESULT: {res.get()}")
if __name__ == "__main__":
main()
Re-run: poetry run python celery_bug.py
Option A — Upgrade to fixed release\npip install celery==5.4.0\nWhen NOT to use: Do not use this fix if your application relies on dict type errbacks in a different context.\n\n
Why This Fix Works in Production
- Trigger: $ poetry run python celery_bug.py
- Mechanism: Fixed a bug where chord.link_error() throws an exception on a dict type errback object, resolving the issue.
- Why the fix works: Fixed a bug where chord.link_error() throws an exception on a dict type errback object, resolving the issue. (first fixed release: 5.4.0).
- If left unfixed, the same config can fail only in production (env differences), causing startup failures or partial feature outages.
Why This Breaks in Prod
- Shows up under Python 3.8 in real deployments (not just unit tests).
- Surfaces as: $ poetry run python celery_bug.py
Proof / Evidence
- GitHub issue: #8678
- Fix PR: https://github.com/celery/celery/pull/8702
- First fixed release: 5.4.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.26
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“Thank you for the bug report! Bugfix merged to main”
“previous related fix https://github.com/celery/celery/pull/8463”
Failure Signature (Search String)
- $ poetry run python celery_bug.py
Error Message
Stack trace
Error Message
-------------
$ poetry run python celery_bug.py
hello world
Traceback (most recent call last):
File "celery_bug.py", line 39, in <module>
main()
File "celery_bug.py", line 35, in main
print(f"RESULT: {res.get()}")
File "/Users/robertgalloway/Library/Caches/pypoetry/virtualenvs/rpg-play-aJQQ1jqR-py3.8/lib/python3.8/site-packages/celery/result.py", line 251, in get
return self.backend.wait_for_pending(
File "/Users/robertgalloway/Library/Caches/pypoetry/virtualenvs/rpg-play-aJQQ1jqR-py3.8/lib/python3.8/site-packages/celery/backends/asynchronous.py", line 223, in wait_for_pending
return result.maybe_throw(callback=callback, propagate=propagate)
File "/Users/robertgalloway/Library/Caches/pypoetry/virtualenvs/rpg-play-aJQQ1jqR-py3.8/lib/python3.8/site-packages/celery/result.py", line 365, in maybe_throw
self.throw(value, self._to_remote_traceback(tb))
File "/Users/robertgalloway/Library/Caches/pypoetry/virtualenvs/rpg-play-aJQQ1jqR-py3.8/lib/python3.8/site-packages/celery/result.py", line 358, in throw
self.on_ready.throw(*args, **kwargs)
File "/Users/robertgalloway/Library/Caches/pypoetry/virtualenvs/rpg-play-aJQQ1jqR-py3.8/lib/python3.8/site-packages/vine/promises.py", line 235, in throw
reraise(type(exc), exc, tb)
File "/Users/robertgalloway/Library/Caches/pypoetry/virtualenvs/rpg-play-aJQQ1jqR-py3.8/lib/python3.8/site-packages/vine/utils.py", line
... (truncated) ...
Minimal Reproduction
from celery import Celery, chain, chord, signature, group
app = Celery(
"celery_bug", backend="redis://0.0.0.0:6479/", broker="redis://0.0.0.0:6479/"
)
app.conf.task_allow_error_cb_on_chord_header = True
@app.task(bind=True)
def orig_task(self, arg):
chord_headers = group([chord_header.s(arg=f"arg{i}") for i in range(5)])
replacement_chord = chord(chord_headers, chord_body.s())
return self.replace(replacement_chord)
@app.task
def chord_header(arg):
return f"header: {arg}"
@app.task
def chord_body(arg):
return f"body: {arg}]"
@app.task
def handle_error(*args, **kwargs):
print(f"handle error called with args {args} kwargs {kwargs}")
def main():
print(f"hello world")
res = orig_task.apply_async(args=["spam"], link_error=handle_error.s())
print(f"RESULT: {res.get()}")
if __name__ == "__main__":
main()
Environment
- Python: 3.8
What Broke
Tasks failed with an AttributeError when using chords with dict type errbacks.
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
pip install celery==5.4.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/8702
First fixed release: 5.4.0
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- Do not use this fix if your application relies on dict type errbacks in a different context.
Verify Fix
Re-run: poetry run python celery_bug.py
Did This Fix Work in Your Case?
Quick signal helps us prioritize which fixes to verify and improve.
Prevention
- Make timeouts explicit and test them (unit + integration) to avoid silent behavior changes.
- Instrument retries (attempt count + reason) and alert on spikes to catch dependency slowdowns.
Version Compatibility Table
| Version | Status |
|---|---|
| 5.4.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.