Context
Starting the web app against a database that holds an account but no operations, budgets or planned operations kills the process: the startup lifespan computes the report, the budget forecast builds an empty frame, and pandas raises TypeError: Cannot infer number of levels from empty list when it turns the (empty) column list into a MultiIndex. Uvicorn logs Application startup failed and exits.
The startup already means to survive this — its helper is documented as "tolerating an empty database" — but it only catches BudgetForecasterError, and this failure arrives as a TypeError from pandas. The same call happens after every write, where the failure would be a 500 instead.
Found while running the app locally against a freshly created database. Not a regression: the empty-data path has presumably never been exercised, since the app is normally started on a database that already has statements imported.
Proposed solution
- Make the budget-forecast frame handle "nothing to forecast": return an empty frame with the right index/column shape instead of feeding an empty list to
MultiIndex.from_tuples.
- Keep the startup catch narrow. Widening it to
Exception would hide the next real failure; the analyzer is what should be total here.
- Check the other report sections for the same assumption (a non-empty index or column set) rather than fixing only the one that raised.
Acceptance criteria
Related issues
Context
Starting the web app against a database that holds an account but no operations, budgets or planned operations kills the process: the startup lifespan computes the report, the budget forecast builds an empty frame, and pandas raises
TypeError: Cannot infer number of levels from empty listwhen it turns the (empty) column list into aMultiIndex. Uvicorn logsApplication startup failedand exits.The startup already means to survive this — its helper is documented as "tolerating an empty database" — but it only catches
BudgetForecasterError, and this failure arrives as aTypeErrorfrom pandas. The same call happens after every write, where the failure would be a 500 instead.Found while running the app locally against a freshly created database. Not a regression: the empty-data path has presumably never been exercised, since the app is normally started on a database that already has statements imported.
Proposed solution
MultiIndex.from_tuples.Exceptionwould hide the next real failure; the analyzer is what should be total here.Acceptance criteria
Related issues