ValidationError 錯誤處理
ValidationError 是 Pydantic 驗證失敗時丟出的例外,它會一次收集所有欄位的錯誤,每筆錯誤都有發生位置(loc)、錯誤類型(type)、訊息(msg)與原始輸入(input)。本頁說明如何讀懂錯誤內容、把錯誤轉成 API 回應,以及自訂錯誤訊息。
ValidationError 裡面有什麼?
from pydantic import BaseModel, Field, ValidationError
class Address(BaseModel):
city: str
zip_code: str = Field(pattern=r"^\d{3,6}$")
class Customer(BaseModel):
name: str
age: int = Field(ge=0)
address: Address
try:
Customer.model_validate({"age": -1, "address": {"city": "台北", "zip_code": "ABC"}})
except ValidationError as e:
print(e.error_count()) # 3
for err in e.errors():
print(err["loc"], err["type"], err["msg"])
輸出:
('name',) missing Field required
('age',) greater_than_equal Input should be greater than or equal to 0
('address', 'zip_code') string_pattern_mismatch String should match pattern '^\d{3,6}$'
| 鍵 | 意義 |
|---|---|
loc |
錯誤位置的 tuple;巢狀模型會顯示完整路徑,例如 ('address', 'zip_code'),串列會出現索引 |
type |
機器可讀的錯誤代碼,例如 missing、int_parsing、greater_than_equal |
msg |
人類可讀的英文訊息 |
input |
造成錯誤的原始輸入值 |
ctx |
部分錯誤附帶的上下文,例如 {'ge': 0} |
怎麼把錯誤轉成 API 回應?
e.errors() 回傳 Python 物件,e.json() 回傳 JSON 字串。回傳給前端時,通常只保留欄位路徑與訊息,並移除 input 以免洩漏敏感資料:
from pydantic import BaseModel, ValidationError
class Login(BaseModel):
email: str
password: str
def validate_login(payload: dict) -> dict:
try:
Login.model_validate(payload)
return {"ok": True}
except ValidationError as e:
return {
"ok": False,
"errors": [
{"field": ".".join(str(p) for p in err["loc"]), "message": err["msg"]}
for err in e.errors(include_input=False, include_url=False)
],
}
print(validate_login({"email": "a@b.c"}))
# {'ok': False, 'errors': [{'field': 'password', 'message': 'Field required'}]}
FastAPI 已內建這個流程,驗證失敗時會自動回傳 HTTP 422 與錯誤清單,見 FastAPI 整合。
可以把錯誤訊息改成中文嗎?
Pydantic 內建訊息是英文。常見做法有兩種:
- 自訂驗證器裡直接寫中文:
raise ValueError("密碼至少要包含一個數字"),訊息會變成Value error, 密碼至少要包含一個數字。 - 依
type對照翻譯:錯誤代碼是穩定的,可以建一張對照表,在回傳前替換訊息。
from pydantic import BaseModel, ValidationError
ZH_MESSAGES = {
"missing": "此欄位為必填",
"int_parsing": "請輸入整數",
"string_too_short": "長度不足",
}
class Form(BaseModel):
age: int
name: str
try:
Form.model_validate({"age": "十八"})
except ValidationError as e:
for err in e.errors():
print(err["loc"][0], ZH_MESSAGES.get(err["type"], err["msg"]))
# age 請輸入整數
# name 此欄位為必填
需要完全控制錯誤代碼與訊息時,可以在驗證器中丟出 pydantic_core.PydanticCustomError("my_code", "自訂訊息 {x}", {"x": 1})。
除錯時要注意什麼?
- 一次看全部錯誤:不要只看第一筆,
ValidationError已經把所有問題都列出來了。 - 看
loc找位置:深層巢狀資料的錯誤,loc會精確指出是第幾筆、哪個欄位。 - 錯誤訊息附的網址:
print(e)會附上https://errors.pydantic.dev/...連結,說明該錯誤類型的成因。 - 不要吞掉例外:
except ValidationError: pass會讓壞資料悄悄消失,至少要記錄 log。
相關資料
- Pydantic 官方文件:Validation Errors:所有錯誤
type的說明 - Pydantic 官方文件:Error Handling
- 下一步:序列化與輸出