Jump to solution
Verify

The Fix

Upgrade to version 0.20.1 or later.

Based on closed Kludex/starlette issue #886 · PR/commit linked

Production note: Most teams hit this during upgrades or environment changes. Roll out with a canary and smoke critical endpoints (health, OpenAPI/docs) before 100%.

Jump to Verify Open PR/Commit
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@ +import asyncio +import functools +import typing
repro.py
def test_async_startup_hangs(): class Foo: async def __call__(self): raise Exception() app = Starlette(on_startup=[Foo()]) with TestClient(app) as client: pass
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\nUpgrade to version 0.20.1 or later.\nWhen NOT to use: This fix is not suitable for synchronous functions that should remain unchanged.\n\nOption C — Workaround\nfor this bug rather than a solution.\nWhen NOT to use: This fix is not suitable for synchronous functions that should remain unchanged.\n\n

Why This Fix Works in Production

  • Trigger: raise Exception()
  • Mechanism: Improper detection of async callables in the startup lifecycle
  • Why the fix works: Improved detection of async callables to ensure they are awaited during startup. (first fixed release: 0.20.1).
Production impact:
  • 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.7.2 in real deployments (not just unit tests).
  • Improper detection of async callables in the startup lifecycle
  • Production symptom (often without a traceback): def test_async_startup_hangs():

Proof / Evidence

  • GitHub issue: #886
  • Fix PR: https://github.com/kludex/starlette/pull/1444
  • First fixed release: 0.20.1
  • Reproduced locally: No (not executed)
  • Last verified: 2026-02-09
  • Confidence: 0.95
  • Did this fix it?: Yes (upstream fix exists)
  • Own content ratio: 0.76

Discussion

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

“TBH I'd be quite keen to moving Starlette towards *always* expecting an async style on endpoints, startup/shutdown functions, error handlers etc”
@lovelydinosaur · 2020-05-07 · source
“@cjw296 An immediate workaround: wrap your callable object into a function? The long-term solution would be to fix the detection of async functions in the…”
@florimondmanca · 2020-03-31 · source
“Turns out this issue also affects partial: https://stackoverflow.com/questions/52422860/partial-asynchronous-functions-are-not-detected-as-asynchronous @tomchristie suggested Starlette should grow its own function to make these checks, once that's happened, we”
@cjw296 · 2020-04-03 · source
“Hi all, I am facing same issues when using **BackgroundTasks** object for my awaitable and callable object, so is there any enhancement for this case…”
@dpr-0 · 2021-07-29 · source

Failure Signature (Search String)

  • raise Exception()
Copy-friendly signature
signature.txt
Failure Signature ----------------- def test_async_startup_hangs(): raise Exception()

Error Message

Signature-only (no traceback captured)
error.txt
Error Message ------------- def test_async_startup_hangs(): raise Exception()

Minimal Reproduction

repro.py
def test_async_startup_hangs(): class Foo: async def __call__(self): raise Exception() app = Starlette(on_startup=[Foo()]) with TestClient(app) as client: pass

Environment

  • Python: 3.7.2

What Broke

Async callables in on_startup do not raise exceptions as expected.

Why It Broke

Improper detection of async callables in the startup lifecycle

Fix Options (Details)

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

Upgrade to version 0.20.1 or later.

When NOT to use: This fix is not suitable for synchronous functions that should remain unchanged.

Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.

Option C — Workaround Temporary workaround

for this bug rather than a solution.

When NOT to use: This fix is not suitable for synchronous functions that should remain unchanged.

Use only if you cannot change versions today. Treat this as a stopgap and remove once upgraded.

Fix reference: https://github.com/kludex/starlette/pull/1444

First fixed release: 0.20.1

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

  • This fix is not suitable for synchronous functions that should remain unchanged.

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
0.20.1 Fixed

Related Issues

No related fixes found.

Sources

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