FastAPI「422 Unprocessable Entity」の原因と直し方
422 Unprocessable Entity は、応答の detail の loc と msg を読み、値を型ヒントに合わせれば直る。
必須項目の欠落(Field required)・型の不一致・JSON 以外での送信が主な原因だ。
公開: 更新:
要約
FastAPI の API を呼ぶと、処理に入る前に 422 Unprocessable Entity が返る。
{"detail":[{"type":"missing","loc":["query","q"],"msg":"Field required","input":null}]}FastAPI は関数の引数に書いた型ヒントでリクエストの値を検証し、合わなければ RequestValidationError を出して 422 を返す(Handling Errors(新しいタブで開く))。
関数の中の処理は実行されない。loc の 1 つ目が値の場所(path・query・body)、2 つ目が項目名で、msg が理由だ。
上の例は、必須のクエリパラメータ q を付けずに呼んだときの応答になる。
この記事の応答は Python 3.12・FastAPI 0.143.0・Pydantic 2.14.1 で実際に返ったものだ。
解決策
1. detail を読んで必須かどうかを決める
loc に出た項目を送るか、送らなくてもよい引数には既定値を付ける。
@app.get("/search")
def search(q: str | None = None, limit: int = 10):
return {"q": q, "limit": limit}この形なら /search は 200 で返る。limit=x のように型に合わない値は、既定値があっても 422 になる。
2. JSON として送る
await fetch("/items", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "pen", price: 100 }),
});3. 包んで送るなら Body(embed=True)
クライアントが {"item": {...}} の形で送る仕様なら、受ける側を合わせる(Body - Multiple Parameters(新しいタブで開く))。
from typing import Annotated
from fastapi import Body
@app.post("/items")
def create_item(item: Annotated[Item, Body(embed=True)]):
return itemこうすると包んだ形は 200 になり、包まずに送った形のほうが ["body","item"] の missing になる。
どちらか一方にそろえる。
よくある原因
- 必須の値が無い: 既定値の無いクエリパラメータは必須になる。
q: strを付け忘れると上のmissingになり、ボディのモデルの項目が欠けるとlocが["body","price"]になる。 - 型に合わない値:
item_id: intにabcを送るとint_parsingで「Input should be a valid integer」になる。"1.5"も整数にできないので同じエラーだが、"100"のような文字列は整数に変換されて通る。 - JSON で送っていない: モデルで受けるボディは JSON として読まれる(Request Body(新しいタブで開く))。
フォーム形式で送ったり、JSON の文字列をContent-Type: text/plainで送ったりすると、locが["body"]の「Input should be a valid dictionary or object to extract fields from」になる。
末尾のカンマなど JSON として壊れているとjson_invalidになる。 - ボディの包み方が違う: モデルが 1 つだけなら、FastAPI はその項目を JSON の最上位で受け取る。
{"item": {...}}と包んで送ると、nameとpriceの両方がmissingになる。