Skip to content

Simulation and report_output routes turn every exception into HTTP 400 #3448

@MaxGhenis

Description

@MaxGhenis

Summary

Several route handlers wrap their work in try: / except Exception as e: raise BadRequest(...). This converts every unexpected failure (database outage, network error, programmer error, etc.) into HTTP 400, which is wrong: 400 tells clients "your request is malformed" and typically disables retry/backoff logic that exists for 5xx.

Location

  • policyengine_api/routes/simulation_routes.py:96-98 (create)
  • policyengine_api/routes/simulation_routes.py:213-215 (update)
  • policyengine_api/routes/report_output_routes.py:96-98 (create)
  • policyengine_api/routes/report_output_routes.py:209-211 (update)

What goes wrong

except Exception as e:
    print(f"Error creating simulation: {str(e)}")
    raise BadRequest(f"Failed to create simulation: {str(e)}")

The same pattern appears four times. Every 5xx-class error (SQL operational error, JSON serialization failure, code bug) is reported to the client as a 400 "Failed to create simulation: ". Clients will not retry; on-call alerts that fire on 5xx rates go silent; and internal error text leaks into the response body.

Suggested fix

Catch only the exceptions that are genuinely user input problems (e.g., KeyError, ValueError, custom validation errors from services) as BadRequest, and let everything else propagate so Flask's default handler returns 500:

except (ValueError, KeyError, pydantic.ValidationError) as e:
    raise BadRequest(f"Invalid input: {e}")
# no general except — 500 is correct for unexpected failures

Also strip str(e) from the client response; log it server-side instead.

Severity

Medium.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions