Skip to content

feat: harden global exception handlers — prevent traceback leakage and unify error responses#434

Open
Arijit429 wants to merge 2 commits intofireform-core:mainfrom
Arijit429:feat/harden-global-exception-handlers
Open

feat: harden global exception handlers — prevent traceback leakage and unify error responses#434
Arijit429 wants to merge 2 commits intofireform-core:mainfrom
Arijit429:feat/harden-global-exception-handlers

Conversation

@Arijit429
Copy link
Copy Markdown

@Arijit429 Arijit429 commented Apr 14, 2026

Closes #82
Closes #145
Closes #311
Closes #394
Closes #295
Closes #374

Summary

Hardens the global exception handling layer to prevent internal stack traces
from leaking to API clients, and unifies all error responses into a consistent
JSON envelope.

Problem

The current handlers.py only catches AppError. Three other error categories
are completely unhandled:

  • RequestValidationError — malformed request bodies return raw Pydantic
    error arrays with internal field paths
  • HTTPExceptiontemplates.py routes use HTTPException which returns
    a different shape than forms.py which uses AppError
  • Unhandled Exception — if Controller() crashes, the full Python stack
    trace including file paths and line numbers is returned to the client

This is a security risk (OWASP: Security Misconfiguration).

Changes

api/errors/handlers.py

  • Added HTTPException handler for consistent error shape
  • Added RequestValidationError handler with human-readable messages
  • Added catch-all Exception handler — logs server-side, returns generic message
  • All handlers return {"success": false, "error": {"code": "...", "message": "..."}}

api/errors/base.py

  • Added missing super().__init__(message) for proper exception chaining

api/routes/forms.py

  • Fixed duplicate get_template() call (was querying database twice per request)
  • Wrapped Controller.fill_form() in try/except for crash protection

api/main.py

  • Ensured register_exception_handlers(app) is called at startup

Testing

Verified all four error paths return uniform JSON:

  • 422 — validation errors (malformed body)
  • 404 — application errors (template not found)
  • 400 — HTTP errors (non-PDF upload)
  • 500 — unhandled errors (generic message, no stack trace)

Existing test suite passes without modification.

Real-world impact

Prevents internal stack traces from leaking to the frontend — important for
CAL FIRE deployment where error logs may be reviewed by non-technical staff.

- Add HTTPException handler for consistent error shape across all routes
- Add RequestValidationError handler with human-readable error messages
- Add catch-all Exception handler to prevent stack trace leakage
- Fix duplicate get_template() call in forms.py (was querying DB twice)
- Wrap Controller errors in AppError for safe client-facing messages
- All errors now return uniform {success, error: {code, message}} envelope
@Arijit429
Copy link
Copy Markdown
Author

Context

The existing handlers.py only caught AppError — three other error types (HTTPException, RequestValidationError, unhandled Exception) were leaking raw internals to the client. This PR adds handlers for all four types with a uniform response shape matching the ErrorResponse schema in common.py.

Also fixed a duplicate get_template() call in forms.py that was hitting the database twice per request.

Copy link
Copy Markdown
Author

@Arijit429 Arijit429 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment