Skip to content

❌ EmailStr rechaza admin@orderflow.local — "special-use or reserved name" ​

Python para Backend · Clase 7 · users_service/scripts/create_admin.py

🩻 Contexto ​

El script create_admin.py crea el usuario admin insertándolo directo con SQLAlchemy (User(...), sin pasar por el schema UserCreate), con el email por defecto admin@orderflow.local — y eso funciona sin problema, el admin se crea.

Pero al intentar loguearse con ese mismo email (POST /api/v1/auth/login, que sí valida el body con el schema LoginRequest, que usa EmailStr), FastAPI responde 422 con:

json
{
  "detail": [{
    "type": "value_error",
    "loc": ["body", "email"],
    "msg": "value is not a valid email address: The part after the @-sign is a
            special-use or reserved name that cannot be used with email."
  }]
}

🔍 Causa ​

.local es un dominio de uso especial/reservado (RFC 6762 — reservado para resolución de nombres en red local vía mDNS/Bonjour, no para correo real). La librería email-validator (la que usa EmailStr de Pydantic por debajo) mantiene una lista de estos dominios reservados (.local, .test, .example, .invalid, .localhost, ...) y los rechaza por defecto, sin importar que el formato usuario@dominio sea sintácticamente válido.

💡 Por qué el admin SÍ se creó pero SÍ falla el login: create_admin.py arma el User(...) directo (sin pasar por UserCreate/EmailStr), así que Pydantic nunca valida ese email al crearlo. LoginRequest, en cambio, SÍ tiene email: EmailStr, y ahí es donde salta el error — la inconsistencia está en que un camino valida y el otro no.

⚠️ Es más grave de lo que parece al principio: EmailStr no solo valida al recibir datos (LoginRequest) — también valida al devolver una respuesta (UserResponse). Mientras ese usuario con .local siga en la tabla, cualquier endpoint que lo incluya en la respuesta explota con 500 Internal Server Error (fastapi.exceptions.ResponseValidationError) — en este proyecto, eso rompió GET /api/v1/users (list_users) para todos los usuarios, no solo para ese uno. La solución real no es solo "no loguearse con ese email" — es no dejar ese dato en la base: DELETE FROM users WHERE email='admin@orderflow.local'; (o UPDATE a un dominio válido).

✅ Solución ​

Usar un dominio que no esté en la lista de reservados al definir ADMIN_EMAIL (variable de entorno que lee el script):

bash
ADMIN_EMAIL=admin@orderflow.dev python3 -m scripts.create_admin
# o cualquier dominio "real" — .dev, .com, .io, etc. — no hace falta que exista de verdad,
# EmailStr valida el FORMATO y que no sea un dominio reservado, no que el dominio responda.

⚠️ Ojo, .com/.org con nombres tipo example.com también están reservados (RFC 2606, para documentación) — evitar example.com, example.org, example.net como dominio de prueba también.

📎 Apuntes relacionados ​