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.
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
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) asBadRequest, and let everything else propagate so Flask's default handler returns 500:Also strip
str(e)from the client response; log it server-side instead.Severity
Medium.