fix: let HTTPException pass through @handle_db_errors #104
No reviewers
Labels
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/major-domo-database#104
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/handle-db-errors-passthrough-http"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
@handle_db_errorswas catching all exceptions including intentionalHTTPException(401, 404, etc.) and re-wrapping them as500 "Database error"POST /transactions/— the bot saw a 500 instead of the actual 401except HTTPException: raiseclause before the genericexcept ExceptionhandlerRoot cause
Discovered during
/ilmovefailure investigation — an API_TOKEN mismatch caused a 401 onPOST /transactions/, but the decorator re-wrapped it as a 500, making the error undiagnosable from the bot side.Test plan
test_query_limits.py— 5/5 passtest_base_service.py— 14/14 pass (1 pre-existing failure unrelated)🤖 Generated with Claude Code
Review: fix: let HTTPException pass through @handle_db_errors
The fix itself is correct and necessary —
HTTPExceptionis a subclass ofException, so the genericexcept Exceptionhandler was catching intentional 401/404 responses and re-wrapping them as 500. Theexcept HTTPException: raiseclause before the generic handler is the right fix, andHTTPExceptionis already imported atdependencies.py:10.However, there is one blocking issue before merging.
Blocking: Integration test asserts the old broken behavior
tests/integration/test_stratplay_routes.py:499was written to document the (now-fixed) broken behavior:crud.py:24raisesHTTPException(status_code=404, ...)for a missing play. Before this PR,@handle_db_errorscaught it and re-raised as 500. After this fix, it correctly returns 404. The assertion on line 499 will fail.Fix needed:
The adjacent
test_week_and_stype_conflict(line 517) assertsr.status_code in (400, 500)— that assertion is already permissive and will continue to pass (returns 400 after the fix).Non-blocking notes
except Exceptionhandler callsdb.close()for actual DB errors. For the newexcept HTTPException: raisepath, no explicit close is called — that's fine because the middleware from PR #87 closes the connection in itsfinallyblock regardless.test_stratplay_routes.pyto the manual checklist.This assertion documents the old broken behavior and will fail after this fix.
GET /plays/999999999now correctly returns 404 (fromcrud.py:24'sHTTPException(status_code=404, ...)), not 500.Change to:
and update the docstring/comment accordingly.
AI Code Review
Files Reviewed
app/dependencies.py(modified)Findings
Correctness
HTTPExceptionsubclassesException, so without this change it was caught by the generic handler and re-wrapped as a 500. The newexcept HTTPException: raiseclause is placed correctly beforeexcept Exception as e:and will intercept all intentional HTTP errors (401, 403, 404, 400, etc.).HTTPExceptionalready imported atapp/dependencies.py:10— no new import needed.db.close()insideexcept Exceptionis no longer reached for HTTP errors. This is safe — the middleware added in PR #87 (now inmain.py) has afinally: db.close()that covers this path.Security
Style & Conventions
Blocker — Integration test documents old (broken) behavior
tests/integration/test_stratplay_routes.py:499must be updated before merging:After this fix,
GET /plays/999999999raises a 404HTTPExceptioninside the route, which now passes through the decorator unchanged. The assertion must change to== 404, and the docstring/comment should be updated to reflect the corrected behavior.Required change:
Non-blocking
test_week_and_stype_conflict(line 517) assertsr.status_code in (400, 500)— this will continue to pass after the fix (400 now passes through correctly). No change needed.Verdict: REQUEST_CHANGES
The core fix is correct and addresses a real observability bug (auth failures silently becoming 500s). One integration test at
tests/integration/test_stratplay_routes.py:499explicitly tests the old broken wrapping behavior and will fail after deployment. Update the assertion to== 404before merging.Automated review by Claude PR Reviewer
Addressed the blocking review feedback in commit
c49f91c:tests/integration/test_stratplay_routes.py:499— updatedtest_get_nonexistent_playto assertr.status_code == 404(was500) and removed the stale docstring/comment that described the old broken wrapping behavior. The test now correctly documents the fixed behavior:HTTPException(404)passes through@handle_db_errorsunchanged.test_week_and_stype_conflict(line 517) was not changed — thein (400, 500)assertion is already permissive and passes with the fix.AI Code Review
Files Reviewed
app/dependencies.py(modified)tests/integration/test_stratplay_routes.py(modified)Findings
Correctness
HTTPExceptionis a subclass ofException, so the originalexcept Exceptionclause caught it and re-wrapped it as a 500. Addingexcept HTTPException: raiseimmediately beforeexcept Exceptionintercepts intentional HTTP errors and re-raises them unchanged. Placement is sound — it comes after thereturn resultsuccess path and before the generic error handler.HTTPExceptionis already imported at line 10 (from fastapi import HTTPException, Response) — no new import required.tests/integration/test_stratplay_routes.pyline 499 now correctly assertsr.status_code == 404(was500), and the docstring and comment have been updated to reflect the fixed behavior.Security
valid_token()checks will now propagate correctly to callers instead of being masked as opaque 500 errors.Style & Conventions
exceptclause is clean and idiomatic. The inline comment (# Let intentional HTTP errors (401, 404, etc.) pass through unchanged) is appropriately explanatory.assert expr, msgpattern preferred by ruff — consistent with project formatting.Suggestions
app/dependencies.py:519is a pre-existing issue tracked separately — out of scope for this PR.Verdict: COMMENT
The fix is correct and the previously-flagged blocker (integration test asserting the old broken
500behavior) has been addressed in this PR. The implementation is minimal, well-placed, and the test update accurately documents the new correct behavior. Approving as COMMENT per convention (Gitea blocks self-approval).Automated review by Claude PR Reviewer
AI Code Review
Files Reviewed
app/dependencies.py(modified)tests/integration/test_stratplay_routes.py(modified)Findings
Correctness
HTTPException(Starlette) is a subclass ofException, so without this clause it was caught by the broadexcept Exceptionhandler and re-wrapped as a 500. Addingexcept HTTPException: raisebeforeexcept Exceptionrestores the intended semantics.HTTPExceptionis already imported atdependencies.py:10— no new import needed.HTTPException: if anHTTPExceptionis raised, it was raised intentionally by the handler (auth check, not found, etc.) with no DB transaction to roll back. The connection middleware (main.py) handlesdb.close()in itsfinallyblock regardless.crud.py:24raisesHTTPException(status_code=404, detail=f"Play ID {play_id} not found")for nonexistent plays. Both the== 404assertion and the"not found" in detailcheck are correct.Security
Style & Conventions
Suggestions
Verdict: COMMENT
Approved — Gitea blocks self-review. Fix is minimal, correct, and well-tested. The blocker from the earlier review (test asserting
== 500) has been resolved.Automated review by Claude PR Reviewer