Jump to solution
Verify

The Fix

pip install fastapi==0.128.4

Based on closed fastapi/fastapi issue #14454 · 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
@@ -2,7 +2,7 @@ from dataclasses import dataclass, field from functools import cached_property, partial -from typing import Any, Callable, List, Optional, Sequence, Union +from typing import Any, Callable, List, Optional, Union
repro.py
# /// script # dependencies = [ # "fastapi[standard]==0.121.3", # ] # /// import json import sys from typing import Annotated import fastapi from fastapi import APIRouter, Depends, FastAPI, Security from fastapi.security import OAuth2AuthorizationCodeBearer, SecurityScopes # print the dependencies print(f'fastapi version = {fastapi.__version__}') print(f'Python version = {sys.version}') app = FastAPI() oauth2_scheme = OAuth2AuthorizationCodeBearer( authorizationUrl='api/oauth/authorize', tokenUrl='/api/oauth/token', refreshUrl='/api/oauth/token', auto_error=False, scopes={'read': 'Read access', 'write': 'Write access'}, ) async def get_token( token: Annotated[str, Depends(oauth2_scheme)], ) -> str: return token AccessToken = Annotated[str, Depends(get_token, use_cache=True)] async def require_oauth_scopes(security_scopes: SecurityScopes, token: AccessToken) -> None: pass async def check_limit(token: AccessToken) -> None: pass # The problem is here. If we remove the dependency from the router, the schema is generated correctly in both fastapi versions. router = APIRouter(prefix='/v1', dependencies=[Depends(check_limit)]) channels_router = APIRouter(prefix='/channels', tags=['Channels']) @channels_router.get("/", dependencies=[Security(require_oauth_scopes, scopes=['read'])]) def read_items(): return {"msg": "You have READ access"} router.include_router(channels_router) app.include_router(router) schema = app.openapi() pretty_schema = json.dumps(schema, indent=4) print(pretty_schema) """ with fastapi 0.123.7, the generated openapi schema icludes security without scopes: "security": [ { "OAuth2AuthorizationCodeBearer": [] } ] but in fastapi 0.121.3, the generated openapi schema includes two items in security: "security": [ { "OAuth2AuthorizationCodeBearer": [] }, { "OAuth2AuthorizationCodeBearer": [ "read" ] } ] """
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 fastapi==0.128.4\nWhen NOT to use: This fix should not be used if the application relies on the previous behavior of duplicated security schemes.\n\n

Why This Fix Works in Production

  • Trigger: OAuth2 security schemes duplicated in OpenAPI, with and without scopes, when used at the router level
  • Mechanism: OAuth2 security schemes were incorrectly duplicated in OpenAPI due to dependency handling
  • Why the fix works: Fix OAuth2 scopes in OpenAPI in extra corner cases, parent dependency with scopes, sub-dependency security scheme without scopes. (first fixed release: 0.128.4).
Production impact:
  • If left unfixed, this can cause silent data inconsistencies that propagate (bad cache entries, incorrect downstream decisions).

Why This Breaks in Prod

  • OAuth2 security schemes were incorrectly duplicated in OpenAPI due to dependency handling
  • Production symptom (often without a traceback): OAuth2 security schemes duplicated in OpenAPI, with and without scopes, when used at the router level

Proof / Evidence

Verified Execution

We executed the runnable minimal repro in a temporary environment and captured exit codes + logs.

  • Status: PASS
  • Ran: 2026-02-11T16:52:29Z
  • Package: fastapi
  • Fixed: 0.128.4
  • Mode: fixed_only
  • Outcome: ok
Logs
affected (exit=None)
fixed (exit=0)
fastapi version = 0.128.4 Python version = 3.10.12 (main, Jan 26 2026, 14:55:28) [GCC 11.4.0] { "openapi": "3.1.0", "info": { "title": "FastAPI", "version": "0.1.0" }, "paths": { "/v1/channels/": { "get": { "tags": [ "Channels" ], "summary": "Read Items", "operationId": "read_items_v1_channels__get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": {} } } } }, "security": [ { "OAuth2AuthorizationCodeBearer": [ "read" ] } ] } } }, "components": { "securitySchemes": { "OAuth2AuthorizationCodeBearer": { "type": "oauth2", "flows": { "authorizationCode": { "refreshUrl": "/api/oauth/token", "scopes": { "read": "Read access", "write": "Write access" }, "authorizationUrl": "api/oauth/authorize", "tokenUrl": "/api/oauth/token" } } } } } }

Discussion

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

“This was fixed by https://github.com/fastapi/fastapi/pull/14455, released in FastAPI 0.123.8 :tada:”
@tiangolo · 2025-12-04 · confirmation · source
“This should be fixed now in https://github.com/fastapi/fastapi/pull/14459 (strike 2 ⚾). Released in FastAPI 0.123.9 🚀”
@tiangolo · 2025-12-04 · confirmation · source
“That didn't fix it. 😱 Working on it now.”
@tiangolo · 2025-12-04 · source

Failure Signature (Search String)

  • OAuth2 security schemes duplicated in OpenAPI, with and without scopes, when used at the router level
  • authorizationUrl='api/oauth/authorize',
Copy-friendly signature
signature.txt
Failure Signature ----------------- OAuth2 security schemes duplicated in OpenAPI, with and without scopes, when used at the router level authorizationUrl='api/oauth/authorize',

Error Message

Signature-only (no traceback captured)
error.txt
Error Message ------------- OAuth2 security schemes duplicated in OpenAPI, with and without scopes, when used at the router level authorizationUrl='api/oauth/authorize',

Minimal Reproduction

repro.py
# /// script # dependencies = [ # "fastapi[standard]==0.121.3", # ] # /// import json import sys from typing import Annotated import fastapi from fastapi import APIRouter, Depends, FastAPI, Security from fastapi.security import OAuth2AuthorizationCodeBearer, SecurityScopes # print the dependencies print(f'fastapi version = {fastapi.__version__}') print(f'Python version = {sys.version}') app = FastAPI() oauth2_scheme = OAuth2AuthorizationCodeBearer( authorizationUrl='api/oauth/authorize', tokenUrl='/api/oauth/token', refreshUrl='/api/oauth/token', auto_error=False, scopes={'read': 'Read access', 'write': 'Write access'}, ) async def get_token( token: Annotated[str, Depends(oauth2_scheme)], ) -> str: return token AccessToken = Annotated[str, Depends(get_token, use_cache=True)] async def require_oauth_scopes(security_scopes: SecurityScopes, token: AccessToken) -> None: pass async def check_limit(token: AccessToken) -> None: pass # The problem is here. If we remove the dependency from the router, the schema is generated correctly in both fastapi versions. router = APIRouter(prefix='/v1', dependencies=[Depends(check_limit)]) channels_router = APIRouter(prefix='/channels', tags=['Channels']) @channels_router.get("/", dependencies=[Security(require_oauth_scopes, scopes=['read'])]) def read_items(): return {"msg": "You have READ access"} router.include_router(channels_router) app.include_router(router) schema = app.openapi() pretty_schema = json.dumps(schema, indent=4) print(pretty_schema) """ with fastapi 0.123.7, the generated openapi schema icludes security without scopes: "security": [ { "OAuth2AuthorizationCodeBearer": [] } ] but in fastapi 0.121.3, the generated openapi schema includes two items in security: "security": [ { "OAuth2AuthorizationCodeBearer": [] }, { "OAuth2AuthorizationCodeBearer": [ "read" ] } ] """

What Broke

OpenAPI schema generated with duplicate security schemes, causing confusion in API documentation.

Why It Broke

OAuth2 security schemes were incorrectly duplicated in OpenAPI due to dependency handling

Fix Options (Details)

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

pip install fastapi==0.128.4

When NOT to use: This fix should not be used if the application relies on the previous behavior of duplicated security schemes.

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

Fix reference: https://github.com/fastapi/fastapi/pull/14459

First fixed release: 0.128.4

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

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • This fix should not be used if the application relies on the previous behavior of duplicated security schemes.

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.128.4 Fixed

Related Issues

No related fixes found.

Sources

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