BUG: API client create/update functions return wrapper object instead of entity — causes page crash
Severity: Critical
Component: Frontend UI / Budget Sources / Subsidy Programs
Found in: E2E tests — budget-sources.spec.ts (create + update scenarios), subsidy-programs.spec.ts (create + update scenarios)
Steps to Reproduce
Budget Sources
- Navigate to
/budget/sources
- Click Add Source
- Fill in a name and amount
- Click Create Source
Subsidy Programs
- Navigate to
/budget/subsidies
- Click Add Program
- Fill in a name and reduction value
- Click Create Program
Expected Behavior
- Create form closes
- Success banner appears with the newly-created entity's name (e.g.
Budget source "My Source" created successfully)
- Entity appears in the list
- Same for edit (update) flows
Actual Behavior
Create flow
The page goes blank (React crashes). No success banner, no list update.
Root cause: createBudgetSource() in client/src/lib/budgetSourcesApi.ts is typed as Promise<BudgetSource> but the server wraps the response in { budgetSource: BudgetSource }. The call site does:
const created = await createBudgetSource(payload);
setSources([...sources, created]); // 'created' is { budgetSource: {...} }
setSuccessMessage(`Budget source "${created.name}" created successfully`); // name = undefined
Then when the list tries to render the malformed entry, getSourceTypeClass(styles, created.sourceType) is called with sourceType = undefined. The function does:
type?.charAt(0).toUpperCase() // type is undefined, optional chain stops here → undefined
// then calls .toUpperCase() on undefined → TypeError!
No ErrorBoundary → entire page goes blank.
Edit (update) flow
The page does NOT crash (the item was created correctly via API and the response is separately fetched). However the success banner reads:
Budget source "undefined" updated successfully
Because updated.name is undefined — the updateBudgetSource function has the same wrong return type.
Affected Files
| File |
Function |
Returns |
Should return |
client/src/lib/budgetSourcesApi.ts |
createBudgetSource |
Promise<BudgetSource> |
unwrapped entity from { budgetSource: BudgetSource } |
client/src/lib/budgetSourcesApi.ts |
updateBudgetSource |
Promise<BudgetSource> |
unwrapped entity from { budgetSource: BudgetSource } |
client/src/lib/subsidyProgramsApi.ts |
createSubsidyProgram |
Promise<SubsidyProgram> |
unwrapped entity from { subsidyProgram: SubsidyProgram } |
client/src/lib/subsidyProgramsApi.ts |
updateSubsidyProgram |
Promise<SubsidyProgram> |
unwrapped entity from { subsidyProgram: SubsidyProgram } |
Fix Options
Option A (preferred): Fix the API client functions to use the correct response type and unwrap in place:
// budgetSourcesApi.ts
export async function createBudgetSource(data: CreateBudgetSourceRequest): Promise<BudgetSource> {
const response = await post<{ budgetSource: BudgetSource }>('/budget-sources', data);
return response.budgetSource;
}
Option B: Fix the call sites in BudgetSourcesPage.tsx / SubsidyProgramsPage.tsx to cast the response correctly.
Environment
- Browser: All (Chromium + WebKit)
- Viewport: All (Desktop, Tablet, Mobile)
- Docker: Not tested (reproducible via dev server)
Evidence
- E2E CI artifact screenshots: blank white page on create (Budget Sources), success banner showing
"undefined" on update
- Test failures: all
Create source and Edit source E2E tests across all viewports (~24 failures total)
Notes
A secondary concern: BudgetSourcesPage.tsx calls getSourceTypeClass(styles, source.sourceType) without guarding against undefined. Even if the API client is fixed, this utility function should handle undefined gracefully (optional chain the full chain, not just the first property access).
[qa-integration-tester] Filed after E2E test investigation. This is a frontend-developer task.
BUG: API client create/update functions return wrapper object instead of entity — causes page crash
Severity: Critical
Component: Frontend UI / Budget Sources / Subsidy Programs
Found in: E2E tests —
budget-sources.spec.ts(create + update scenarios),subsidy-programs.spec.ts(create + update scenarios)Steps to Reproduce
Budget Sources
/budget/sourcesSubsidy Programs
/budget/subsidiesExpected Behavior
Budget source "My Source" created successfully)Actual Behavior
Create flow
The page goes blank (React crashes). No success banner, no list update.
Root cause:
createBudgetSource()inclient/src/lib/budgetSourcesApi.tsis typed asPromise<BudgetSource>but the server wraps the response in{ budgetSource: BudgetSource }. The call site does:Then when the list tries to render the malformed entry,
getSourceTypeClass(styles, created.sourceType)is called withsourceType = undefined. The function does:No ErrorBoundary → entire page goes blank.
Edit (update) flow
The page does NOT crash (the item was created correctly via API and the response is separately fetched). However the success banner reads:
Because
updated.nameisundefined— theupdateBudgetSourcefunction has the same wrong return type.Affected Files
client/src/lib/budgetSourcesApi.tscreateBudgetSourcePromise<BudgetSource>{ budgetSource: BudgetSource }client/src/lib/budgetSourcesApi.tsupdateBudgetSourcePromise<BudgetSource>{ budgetSource: BudgetSource }client/src/lib/subsidyProgramsApi.tscreateSubsidyProgramPromise<SubsidyProgram>{ subsidyProgram: SubsidyProgram }client/src/lib/subsidyProgramsApi.tsupdateSubsidyProgramPromise<SubsidyProgram>{ subsidyProgram: SubsidyProgram }Fix Options
Option A (preferred): Fix the API client functions to use the correct response type and unwrap in place:
Option B: Fix the call sites in
BudgetSourcesPage.tsx/SubsidyProgramsPage.tsxto cast the response correctly.Environment
Evidence
"undefined"on updateCreate sourceandEdit sourceE2E tests across all viewports (~24 failures total)Notes
A secondary concern:
BudgetSourcesPage.tsxcallsgetSourceTypeClass(styles, source.sourceType)without guarding againstundefined. Even if the API client is fixed, this utility function should handleundefinedgracefully (optional chain the full chain, not just the first property access).[qa-integration-tester] Filed after E2E test investigation. This is a frontend-developer task.