failed WRONG_FIX UNSUBMITTED wrong_fix_unsubmitted(None) · None tool calls · 0 s · fastapi/fastapi
🐛 Fix using `Json[list[str]]` type (issue #10997)
Fixes Form() not working with Pydantic `Json[T]` types when T is a sequence type (list, set, tuple).
Closes #10997
When using `Form()` with `Json[list[str]]`, FastAPI incorrectly treated it as a sequence field and used `getlist()`, wrapping the JSON string in a list. This caused Pydantic to fail parsing with:
```
TypeError: JSON input should be string, bytes or bytearray
```
**Before fix:**
```python
# Client sends
data = {"items": '["abc", "def"]'} # JSON string
# FastAPI incorrectly does
value = values.getlist("items") # ['["abc", "def"]'] ❌
```
**After fix:**
```python
# FastAPI correctly does
value = values.get("items") # '["abc", "def"]' ✅
```
This fix enables legitimate use cases like file uploads with JSON metadata:
```python
@app.post("/upload")
def upload(
file: UploadFile,
tags: Annotated[Json[list[str]], Form()]
):
return {"file": file.filename, "tags": tags}
```
Pydantic v1 support for `Json[T]` with `Form()` is not included in this PR because `metadata` attribute does not exist in Pydantic v1's `FieldInfo`.
## Related
- Issue #10997| # | Tool | Arguments | Result |
|---|---|---|---|
| No trace captured. | |||
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -51,7 +51,7 @@
from fastapi.security.oauth2 import SecurityScopes
from fastapi.types import DependencyCacheKey
from fastapi.utils import create_model_field, get_path_param_names
-from pydantic import BaseModel
+from pydantic import BaseModel, Json
from pydantic.fields import FieldInfo
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
from starlette.concurrency import run_in_threadpool
@@ -726,11 +726,19 @@ def _validate_value_with_model_field(
return v_, []
+def _is_json_field(field: ModelField) -> bool:
+ return any(type(item) is Json for item in field.field_info.metadata)
+
+
def _get_multidict_value(
field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
) -> Any:
alias = alias or get_validation_alias(field)
- if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
+ if (
+ (not _is_json_field(field))
+ and is_sequence_field(field)
+ and isinstance(values, (ImmutableMultiDict, Headers))
+ ):
value = values.getlist(alias)
else:
value = values.get(alias, None)
==================================== ERRORS ====================================
___________________ ERROR collecting tests/test_json_type.py ___________________
tests/test_json_type.py:11: in <module>
@app.post("/form-json-list")
^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi/routing.py:1063: in decorator
self.add_api_route(
fastapi/routing.py:1002: in add_api_route
route = route_class(
fastapi/routing.py:621: in __init__
self.dependant = get_dependant(
fastapi/dependencies/utils.py:282: in get_dependant
param_details = analyze_param(
fastapi/dependencies/utils.py:505: in analyze_param
ensure_multipart_is_installed()
fastapi/dependencies/utils.py:110: in ensure_multipart_is_installed
raise RuntimeError(multipart_not_installed_error) from None
E RuntimeError: Form data requires "python-multipart" to be installed.
E You can install "python-multipart" with:
E
E pip install python-multipart
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
_PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 warning, 1 error in 0.79s