{"record":{"id":"2b2911eb4d84f44e","repo":"affaan-m/ECC","slug":"email-already-registered","errorCode":null,"errorMessage":"Email already registered","messagePattern":"Email already registered","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"skills/fastapi-patterns/SKILL.md","lineNumber":248,"sourceCode":"# app/routers/users.py\nfrom typing import Annotated\nfrom fastapi import APIRouter, HTTPException, Query, status\nfrom fastapi.security import OAuth2PasswordRequestForm\n\nfrom app.dependencies import ActiveUserDep, DbDep\nfrom app.schemas.user import UserCreate, UserResponse, UserUpdate, UserListResponse\nfrom app.services.user_service import DuplicateUserError, UserService\n\nrouter = APIRouter()\n\n\n@router.post(\"/\", response_model=UserResponse, status_code=status.HTTP_201_CREATED)\nasync def create_user(payload: UserCreate, db: DbDep) -> UserResponse:\n    service = UserService(db)\n    try:\n        return await service.create(payload)\n    except DuplicateUserError:\n        raise HTTPException(status_code=400, detail=\"Email already registered\")\n\n\n@router.get(\"/me\", response_model=UserResponse)\nasync def get_me(current_user: ActiveUserDep) -> UserResponse:\n    return current_user\n\n\n@router.get(\"/\", response_model=UserListResponse)\nasync def list_users(\n    db: DbDep,\n    current_user: ActiveUserDep,\n    skip: Annotated[int, Query(ge=0)] = 0,\n    limit: Annotated[int, Query(ge=1, le=100)] = 20,\n) -> UserListResponse:\n    service = UserService(db)\n    users, total = await service.list(skip=skip, limit=limit)\n    return UserListResponse(total=total, items=users)\n","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/fastapi-patterns/SKILL.md#L230-L266","documentation":"In the `create_user` route handler, the service layer raises a domain exception `DuplicateUserError`; the handler maps it to `HTTPException(400, \"Email already registered\")`. The 400 (not 409) is the documented contract for this codebase — `DuplicateUserError` is the canonical \"email taken\" signal from `UserService.create`.","triggerScenarios":"POST `/users/` (or `/`) with an `email` that already exists in the users table — `UserService.create` inserts, hits the unique constraint, and raises `DuplicateUserError`. Race condition: two concurrent registrations for the same email.","commonSituations":"User registers twice (typo, forgot they had an account). Test suite does not clean up users between runs. Seed data inserts an email a test then tries to create.","solutions":["Front-end: on 400 with this detail, show \"an account with this email already exists\" and offer login/password-reset.","Confirm there is a unique constraint on `users.email` at the DB level — without it `DuplicateUserError` can never fire and you get a silent second row.","Make sure `UserService.create` actually raises `DuplicateUserError` on `IntegrityError` (and not a generic 500).","For races, consider 409 Conflict if your API standard prefers it — but keep the service exception the source of truth."],"exampleFix":"# before\nexcept DuplicateUserError:\n    raise HTTPException(status_code=400, detail=\"Email already registered\")\n\n# after — 409 is more conventional for conflict; keep detail stable for clients\nexcept DuplicateUserError:\n    raise HTTPException(status_code=409, detail=\"Email already registered\")","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"from fastapi import HTTPException\ntry:\n    resp = await client.post('/users/', json=payload)\nexcept HTTPException as e:\n    if e.status_code == 400 and 'already registered' in e.detail:\n        suggest_login(payload['email'])\n    raise","preventionTips":["Guarantee a DB unique constraint on `users.email` so the service can detect duplicates reliably.","Map `DuplicateUserError` to HTTP in one place (exception handler) instead of per route.","Free-text email availability check (debounced) before submit to reduce 400s."],"tags":["fastapi","auth","registration","http-400","services"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}