Skip to content

bug: email-login endpoints return 500 on a malformed body (unauthenticated /api/auth/email/request and /verify) #3676

Closed

Description

@vybe

Summary

The two unauthenticated email-login endpoints, POST /api/auth/email/request and POST /api/auth/email/verify, return 500 Internal Server Error for a malformed body instead of a 4xx. A body missing a required field, a body that is not a JSON object, or a body that is not JSON at all all hit this. Under the integration suite the same failure sometimes surfaces as a dropped connection ("Server disconnected without sending a response") instead of a 500.

Reproduction

curl -s -X POST http://localhost:8000/api/auth/email/request -H 'Content-Type: application/json' -d '{}'
# => 500 Internal Server Error

curl -s -X POST http://localhost:8000/api/auth/email/verify -H 'Content-Type: application/json' -d '{"email":"test@example.com"}'
# => 500 Internal Server Error

curl -s -X POST http://localhost:8000/api/auth/email/verify -H 'Content-Type: application/json' -d 'not json'
# => 500 Internal Server Error

The backend log shows an unhandled pydantic_core.ValidationError from EmailLoginVerify(**body).

Context

Both handlers in src/backend/routers/auth.py take a raw Request, then run body = await request.json() and build the model by hand (EmailLoginRequest(**body), EmailLoginVerify(**body)) with no exception handling. A ValidationError, a JSON decode error or a non-dict body therefore escapes as a 500.

Why it matters:

  • These are pre-auth, internet-facing endpoints. Any anonymous caller can produce server errors and full tracebacks in the error log on demand. That is log noise and an alerting false-positive source.
  • The full integration run fails tests/test_email_auth.py intermittently. test_request_code_missing_email_returns_error and test_verify_code_invalid_code_returns_401 both failed with RemoteProtocolError in recent full runs.

src/backend/routers/settings/whitelist.py (add_to_whitelist, #3455) already solves the same shape. It wraps the parse, maps ValidationError to 422, and rejects a non-object body with a one-sentence 422.

Acceptance Criteria

  • POST /api/auth/email/request returns 422, not 500, for {}, for a non-object JSON body, and for a non-JSON body.
  • POST /api/auth/email/verify returns 422, not 500, for a body missing email or code, for a non-object JSON body, and for a non-JSON body.
  • The 422 path for /request keeps the SEC: User and Agent Enumeration via differential API responses #186 enumeration-uniformity guarantee. A malformed body must not reveal anything about whether an address exists.
  • A malformed body to either endpoint logs no traceback at ERROR level.
  • Unit tests cover each malformed-body case for both endpoints.

Technical Notes

Activity

  1. added
    status-readyGreenlit and ready for development (vetted; counterpart to status-incubating)
    on Oct 13, 2026
  2. added and removed
    status-readyGreenlit and ready for development (vetted; counterpart to status-incubating)
    on Oct 19, 2026
  3. self-assigned this
    on Oct 19, 2026
  4. obasilakis commented on Oct 19, 2026

    @obasilakis
    Contributor

    /claim

  5. github-actions commented on Oct 19, 2026

    @github-actions

    @obasilakis This issue is already assigned to obasilakis. Use /unclaim first if you want to take over.

  6. added
    status-in-devMerged to dev, awaiting release cut to main
    and removed on Oct 19, 2026
  7. removed
    status-in-devMerged to dev, awaiting release cut to main
    on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions