Jump to solution
Verify

The Fix

pip install celery==5.5.0

Based on closed celery/celery issue #8976 · 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.

Jump to Verify Open PR/Commit
@@ -12,10 +12,10 @@ class DjangoTask(Task): """ - def delay_on_commit(self, *args, **kwargs): + def delay_on_commit(self, *args, **kwargs) -> None: """Call :meth:`~celery.app.task.Task.delay` with Django's ``on_commit()``."""
repro.py
class DjangoTask(Task): """ Extend the base :class:`~celery.app.task.Task` for Django. Provide a nicer API to trigger tasks at the end of the DB transaction. """ def delay_on_commit(self, *args, **kwargs): """Call :meth:`~celery.app.task.Task.delay` with Django's ``on_commit()``.""" return transaction.on_commit(functools.partial(self.delay, *args, **kwargs)) def apply_async_on_commit(self, *args, **kwargs): """Call :meth:`~celery.app.task.Task.apply_async` with Django's ``on_commit()``.""" return transaction.on_commit(functools.partial(self.apply_async, *args, **kwargs))
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
Option A — Upgrade to fixed release\npip install celery==5.5.0\nWhen NOT to use: Do not use delay_on_commit if you need the task UUID immediately.\n\n

Why This Fix Works in Production

  • Trigger: - [ ] I have included all related issues and possible duplicate issues
  • Mechanism: The delay_on_commit method was incorrectly returning None instead of the task UUID
  • Why the fix works: Clarifies the return values for the delay_on_commit and apply_async_on_commit methods, ensuring they return None instead of a task UUID. (first fixed release: 5.5.0).
Production impact:
  • If left unfixed, retries/timeouts can trigger duplicate external side-effects (double charges, duplicate emails, repeated writes).

Why This Breaks in Prod

  • The delay_on_commit method was incorrectly returning None instead of the task UUID
  • Production symptom (often without a traceback): - [ ] I have included all related issues and possible duplicate issues

Proof / Evidence

  • GitHub issue: #8976
  • Fix PR: https://github.com/celery/celery/pull/8984
  • First fixed release: 5.5.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.52

Discussion

High-signal excerpts from the issue thread (symptoms, repros, edge-cases).

“> > [x] I have tried to reproduce the issue with pytest-celery and added the reproduction script below. > > Where is it? oh no…”
@FraCata00 · 2024-04-24 · repro detail · source
“> [x] I have tried to reproduce the issue with pytest-celery and added the reproduction script below. Where is it?”
@Nusnus · 2024-04-24 · repro detail · source
“> so if I need the task UUID just use mandatory the delay or apply_async call task method, correctly? Correct, yes”
@browniebroke · 2024-04-24 · source
“I think that django transaction.on_commit should return None, but this is the source of django transaction,py transaction.py if use the decorator instead the on_commit function?…”
@FraCata00 · 2024-04-24 · source

Failure Signature (Search String)

  • - [ ] I have included all related issues and possible duplicate issues
  • or possible duplicates to this issue as requested by the checklist above.
Copy-friendly signature
signature.txt
Failure Signature ----------------- - [ ] I have included all related issues and possible duplicate issues or possible duplicates to this issue as requested by the checklist above.

Error Message

Signature-only (no traceback captured)
error.txt
Error Message ------------- - [ ] I have included all related issues and possible duplicate issues or possible duplicates to this issue as requested by the checklist above.

Minimal Reproduction

repro.py
class DjangoTask(Task): """ Extend the base :class:`~celery.app.task.Task` for Django. Provide a nicer API to trigger tasks at the end of the DB transaction. """ def delay_on_commit(self, *args, **kwargs): """Call :meth:`~celery.app.task.Task.delay` with Django's ``on_commit()``.""" return transaction.on_commit(functools.partial(self.delay, *args, **kwargs)) def apply_async_on_commit(self, *args, **kwargs): """Call :meth:`~celery.app.task.Task.apply_async` with Django's ``on_commit()``.""" return transaction.on_commit(functools.partial(self.apply_async, *args, **kwargs))

What Broke

Users were unable to retrieve task UUIDs when using delay_on_commit, leading to confusion.

Why It Broke

The delay_on_commit method was incorrectly returning None instead of the task UUID

Fix Options (Details)

Option A — Upgrade to fixed release Safe default (recommended)

pip install celery==5.5.0

When NOT to use: Do not use delay_on_commit if you need the task UUID immediately.

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/8984

First fixed release: 5.5.0

Last verified: 2026-02-09. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • Do not use delay_on_commit if you need the task UUID immediately.

Verify Fix

verify
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

VersionStatus
5.5.0 Fixed

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.