Skip to content

bug: App crashes when budget has empty capacities array #211

@moltboie

Description

@moltboie

Bug Description

The app crashes with TypeError: Cannot read properties of undefined (reading 'month') when a budget, section, or category exists with an empty capacities array.

Error location: LabeledBar/index.tsx:40barData.getActiveCapacity(date) returns undefined when capacities is empty.

Root Cause

Two issues combine:

1. Subclass constructors wipe the default capacity

BudgetFamily constructor correctly adds a fallback capacity when capacities is empty:

// BudgetFamily constructor
assign(this, init);
this.fromJSON();
if (!this.capacities.length) this.capacities = [new Capacity()]; // ✅ Adds default

But all three subclasses (Budget, Section, Category) re-assign from init after calling super():

// Budget/Section/Category constructor
super(init);           // ✅ BudgetFamily adds default capacity
assign(this, init);    // ❌ Overwrites capacities with the original [] from init
this.fromJSON();       // Maps over empty array → still empty

The second assign(this, init) overwrites the default capacity added by the parent constructor.

2. Server creates budgets with empty capacities

createBudget in budgets.ts uses data.capacities || [] — creating budgets with no capacities in the database.

Reproduction

  1. The DB currently has two budgets with capacities: []:
    • 86a6f730-5a72-4ec6-9415-6446a18d87d8 (New Budget)
    • 540b7170-5c5b-42b0-941a-f1d814db116b (New Budget)
  2. Loading the app (budgets page) crashes immediately

Proposed Fix

Client: In getActiveCapacity, return a default new Capacity() when capacities is empty (defensive), and fix the subclass constructors to not re-assign capacities from init (root cause).

Server: createBudget should provide a default capacity when none is specified.

Data cleanup: Update the two existing budgets with empty capacities:

UPDATE budgets SET capacities = '[{"month": 0, "capacity_id": "default"}]' WHERE capacities = '[]';

Impact

  • Severity: High — crashes the entire app (error boundary catches but no recovery)
  • Scope: Affects any user who creates a budget without immediately configuring a capacity
  • Persistence: IndexedDB caches the broken data, so clearing browser state also fails until IDB is manually wiped

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions