fastapi - 💡(How to fix) Fix Broken optional orjson/ujson install can break importing fastapi.responses

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Recent fastapi.responses code catches ModuleNotFoundError when importing optional JSON response encoders:

try:
    ujson = cast(_UjsonModule, importlib.import_module("ujson"))
except ModuleNotFoundError:
    ujson = None

try:
    orjson = cast(_OrjsonModule, importlib.import_module("orjson"))
except ModuleNotFoundError:
    orjson = None

This handles the package-absent case, but it no longer handles the optional C-extension failure case where the module exists but cannot load its binary extension or a platform dependency. In that case, importlib.import_module("orjson") or importlib.import_module("ujson") can raise a plain ImportError, and importing fastapi.responses fails even if the user only needs JSONResponse, HTMLResponse, etc.

I noticed this while reviewing recent changes around free-threaded Python 3.14t support and the responses.py optional import cleanup.

Expected behavior: a broken optional orjson or ujson install should not break importing the general response module. It should behave like the package is unavailable until the deprecated optional response class is actually used.

Suggested fix: catch ImportError for these optional imports, as the previous direct imports did, or catch both ModuleNotFoundError and ImportError.

Suggested regression test: monkeypatch importlib.import_module to raise ImportError for orjson, then assert import fastapi.responses succeeds.

Error Message

try: ujson = cast(_UjsonModule, importlib.import_module("ujson")) except ModuleNotFoundError: ujson = None

try: orjson = cast(_OrjsonModule, importlib.import_module("orjson")) except ModuleNotFoundError: orjson = None

Root Cause

Recent fastapi.responses code catches ModuleNotFoundError when importing optional JSON response encoders:

try:
    ujson = cast(_UjsonModule, importlib.import_module("ujson"))
except ModuleNotFoundError:
    ujson = None

try:
    orjson = cast(_OrjsonModule, importlib.import_module("orjson"))
except ModuleNotFoundError:
    orjson = None

This handles the package-absent case, but it no longer handles the optional C-extension failure case where the module exists but cannot load its binary extension or a platform dependency. In that case, importlib.import_module("orjson") or importlib.import_module("ujson") can raise a plain ImportError, and importing fastapi.responses fails even if the user only needs JSONResponse, HTMLResponse, etc.

I noticed this while reviewing recent changes around free-threaded Python 3.14t support and the responses.py optional import cleanup.

Expected behavior: a broken optional orjson or ujson install should not break importing the general response module. It should behave like the package is unavailable until the deprecated optional response class is actually used.

Suggested fix: catch ImportError for these optional imports, as the previous direct imports did, or catch both ModuleNotFoundError and ImportError.

Suggested regression test: monkeypatch importlib.import_module to raise ImportError for orjson, then assert import fastapi.responses succeeds.

Fix Action

Fix / Workaround

Suggested regression test: monkeypatch importlib.import_module to raise ImportError for orjson, then assert import fastapi.responses succeeds.

Code Example

import sys, importlib

sys.path.insert(0, "/path/to/fastapi")

real_import_module = importlib.import_module

def fake_import_module(name, package=None):
    if name == "ujson":
        raise ModuleNotFoundError("No module named 'ujson'")
    if name == "orjson":
        raise ImportError("simulated binary/load failure")
    return real_import_module(name, package)

importlib.import_module = fake_import_module

import fastapi.responses

---

try:
    ujson = cast(_UjsonModule, importlib.import_module("ujson"))
except ModuleNotFoundError:
    ujson = None

try:
    orjson = cast(_OrjsonModule, importlib.import_module("orjson"))
except ModuleNotFoundError:
    orjson = None
RAW_BUFFERClick to expand / collapse

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google "How to X in FastAPI" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options.

Example Code

import sys, importlib

sys.path.insert(0, "/path/to/fastapi")

real_import_module = importlib.import_module

def fake_import_module(name, package=None):
    if name == "ujson":
        raise ModuleNotFoundError("No module named 'ujson'")
    if name == "orjson":
        raise ImportError("simulated binary/load failure")
    return real_import_module(name, package)

importlib.import_module = fake_import_module

import fastapi.responses

Description

Recent fastapi.responses code catches ModuleNotFoundError when importing optional JSON response encoders:

try:
    ujson = cast(_UjsonModule, importlib.import_module("ujson"))
except ModuleNotFoundError:
    ujson = None

try:
    orjson = cast(_OrjsonModule, importlib.import_module("orjson"))
except ModuleNotFoundError:
    orjson = None

This handles the package-absent case, but it no longer handles the optional C-extension failure case where the module exists but cannot load its binary extension or a platform dependency. In that case, importlib.import_module("orjson") or importlib.import_module("ujson") can raise a plain ImportError, and importing fastapi.responses fails even if the user only needs JSONResponse, HTMLResponse, etc.

I noticed this while reviewing recent changes around free-threaded Python 3.14t support and the responses.py optional import cleanup.

Expected behavior: a broken optional orjson or ujson install should not break importing the general response module. It should behave like the package is unavailable until the deprecated optional response class is actually used.

Suggested fix: catch ImportError for these optional imports, as the previous direct imports did, or catch both ModuleNotFoundError and ImportError.

Suggested regression test: monkeypatch importlib.import_module to raise ImportError for orjson, then assert import fastapi.responses succeeds.

Operating System

Windows

Operating System Details

Windows 11, local repo checkout

FastAPI Version

Current master at ecace740

Python Version

Python 3.12.7

Additional Context

Related recent PR reviewed: #15149 (4b264878), "Support free-threaded Python 3.14t".

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING