affaan-m/ECC · warning · ValueError
Passwords do not match
Error message
Passwords do not match
What it means
A Pydantic v2 `model_validator(mode="after")` on the `UserCreate` schema raises `ValueError` when `password != password_confirm`. Pydantic converts a validator `ValueError` into a `ValidationError` that FastAPI surfaces as HTTP 422 with a field-level error. `password_confirm` is a transport-only field and is not persisted.
Source
Thrown at skills/fastapi-patterns/SKILL.md:138
```python
# app/schemas/user.py
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field, model_validator
class UserBase(BaseModel):
email: EmailStr
username: str = Field(min_length=3, max_length=50)
class UserCreate(UserBase):
password: str = Field(min_length=8)
password_confirm: str
@model_validator(mode="after")
def passwords_match(self) -> "UserCreate":
if self.password != self.password_confirm:
raise ValueError("Passwords do not match")
return self
class UserUpdate(BaseModel):
username: str | None = Field(default=None, min_length=3, max_length=50)
email: EmailStr | None = None
class UserResponse(UserBase):
id: int
is_active: bool
created_at: datetime
model_config = {"from_attributes": True}
class UserListResponse(BaseModel):
total: intView on GitHub (pinned to 01e15490f0)
Solutions
- Check the request body: both fields present and equal before submit (client-side `password === passwordConfirm`).
- If the 422 lists the error under `password_confirm`, the values differ — re-enter and resubmit.
- Confirm the schema field name matches what the client sends (`password_confirm`, not `passwordConfirm` or `confirm_password`).
- If you do not want a 422, drop the validator and rely on a server-side hash+compare; but the mismatch check is the recommended pattern.
Example fix
# before
@model_validator(mode="after")
def passwords_match(self) -> "UserCreate":
if self.password != self.password_confirm:
raise ValueError("Passwords do not match")
return self
# after — clear, attributed error so FastAPI's 422 points at the right field
from pydantic import model_validator
@model_validator(mode="after")
def passwords_match(self) -> "UserCreate":
if self.password != self.password_confirm:
raise ValueError({"password_confirm": "Passwords do not match"})
return self Defensive patterns
Strategy: validation
Validate before calling
# client-side, before submit
if form.password !== form.password_confirm:
setFieldError('password_confirm', 'Passwords do not match')
return
await fetch('/users/', {method:'POST', body: JSON.stringify(form)}) Type guard
from pydantic import BaseModel
def has_matching_passwords(data: dict) -> bool:
return isinstance(data, dict) and data.get('password') == data.get('password_confirm') and bool(data.get('password')) Try / catch
from pydantic import ValidationError
try:
user_in = UserCreate(**payload)
except ValidationError as e:
# 422 body; surface password_confirm error to that input
return json_response(e.errors(), status=422) Prevention
- Disable submit until both password fields are non-empty and equal.
- Keep the field name `password_confirm` consistent across client and server.
- Drop `password_confirm` before persistence (model_dump(exclude={'password_confirm'})).
When it happens
Trigger: POST `/users/` (or `/`) with a JSON body where `password` and `password_confirm` differ, or where `password_confirm` is missing (Pydantic raises a missing-field error first). Front-end sends a stale form after the user edited one password box but not the second.
Common situations: JS client only sends `password` and forgets `password_confirm` (422 on missing field, not the mismatch). Two password inputs get out of sync due to autofill or paste. Tests construct `UserCreate(...)` directly with mismatched values.
Related errors
- --no-browser is valid only for ecc ito login; auth is valida
- Inactive user
- Email already registered
- Not authorized
- Incorrect username or password
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/a66ddefc7cf9d24b.
Report an issue: GitHub.