SIGN IN SIGN UP

fastapi: fix AttributeError on partially matched routes with FastAPI 0.137 included routers (#4700)

* fastapi: handle FastAPI 0.137 _IncludedRouter routes

FastAPI 0.137 changed ``app.routes`` from a flat list into a tree that
nests ``include_router()`` routes under ``_IncludedRouter`` nodes. These
nodes have no ``path`` attribute, so ``_get_route_details()`` raised
``AttributeError`` when resolving the route for any included route.

Detect these nodes via their ``effective_route_contexts()`` and match
the flattened contexts directly. This avoids the crash, reports the full
templated route, and (by not calling ``_IncludedRouter.matches()``)
avoids re-deriving the request scope.

Fixes #4699

Assisted-by: Claude Opus 4.8

* fastapi: test route details for include_router routes

Add a regression test asserting that a request to an
``include_router()``-added route reports the templated ``http.route``
and does not raise ``AttributeError`` on FastAPI 0.137+.

Assisted-by: Claude Opus 4.8

* fastapi: bump tested FastAPI to 0.137.1

Pin the test dependencies to FastAPI 0.137.1 and the Starlette, pydantic
and anyio versions it requires, so the suite exercises the 0.137 route
tree.

Assisted-by: Claude Opus 4.8

* fastapi: flatten included routes to dedupe route matching

Addresses review feedback: the _IncludedRouter branch repeated the same
Match.FULL/Match.PARTIAL handling as the regular-route path. Extract the
expansion into a _flatten_routes() generator that yields effective route
contexts for _IncludedRouter nodes and the route itself otherwise, so
_get_route_details() runs a single match-and-extract loop over a uniform
list of candidates.

No behavior change; 105 tests pass on FastAPI 0.137.1.

Assisted-by: Claude Opus 4.8

* fastapi: use public iter_route_contexts() to flatten routes

FastAPI 0.137.2 added the public iter_route_contexts() helper for exactly
the "walk the routes to find a match" use case, flattening include_router()
trees (the 0.137 _IncludedRouter nodes) into RouteContext objects that
expose matches() and the templated path. Prefer it over reaching into the
private _IncludedRouter.effective_route_contexts() / _EffectiveRouteContext
internals.

_flatten_routes() now uses iter_route_contexts() when available and falls
back to the previous behavior otherwise, so FastAPI 0.137.0/0.137.1 (which
have _IncludedRouter but not the public helper) and versions before 0.137
(plain routes) keep working. The single match/extract loop is unchanged.

Bump the tested FastAPI to 0.137.2 (starlette stays at 1.3.1). Verified
105 passed on 0.137.2 (public path), 0.137.1 (effective_route_contexts
fallback), and 0.126.0 (plain-routes fallback).

Assisted-by: Claude Opus 4.8

* Rework test-requirements to test multiple versions

* Update workflows

* fastapi: rename changelog fragment to PR number

The changelog check requires the fragment to be named after the PR
number (.changelog/<PR_NUMBER>.<type>); rename 4699.fixed -> 4700.fixed.

* Try to please requirements for pypy

---------

Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
K
Kshitiz Jain committed
5b79cbdfc7e5362cfa95b7abaf412fad23ae2248
Parent: a5081cd
Committed by GitHub <noreply@github.com> on 6/22/2026, 7:09:38 AM