Contents
In brief
While building a self-hosted AI Personal OS, the author deleted a log row in Firebase Realtime Database. The backend returned 200 OK, but React crashed with items.map is not a function. RTDB stores “arrays” as objects with numeric keys; after a gap appears, the SDK may stop coercing to a real array.
What happened
Stack: Firebase RTDB for live state sync, React for the dashboard. One Delete in the Firebase console removed the row, yet the UI went blank.
Console: TypeError: items.map is not a function — the list component assumed an array and received a plain object.
Realtime Database is a JSON tree with no native array type. Arrays become objects keyed "0", "1", "2". While keys stay dense, the client SDK often returns a real Array. Delete a middle element and indices gap; the SDK may return a raw object. React list code (.map, .filter, spreads) then breaks or takes down the whole tree.
A common trap: TypeScript types the field as string[], but runtime delivers { "0": "a", "2": "c" } with no "1".
Why it matters
Any RTDB + React app that renders lists from synced data can hit the same bug. The Firebase console still “looks like” an array while the wire format stays an object — that mismatch breeds false confidence.
Lesson: cloud truth does not guarantee client shape. You need normalization at read boundaries, especially for user-edited lists (delete, reorder).
In practice
- Normalize on every snapshot/listener — do not trust
Array.isArrayafter RTDB mutations. - For numeric-key objects, use
Object.values(data).filter(Boolean)or sort keys then map; pick what fits sparse vs dense lists. - Centralize a TypeScript guard
asList<T>(v: unknown): T[]before render. - For critical collections, consider Firestore with explicit documents, or store
id → itemplus a separate ordered id list. - Add an integration test: write
[a,b,c], deleteb, read snapshot — assert the UI still renders.
Takeaway
One RTDB delete can brick the frontend via implicit typing. The fix is defensive normalization on the client, not assuming JSON-tree “arrays” behave like JavaScript arrays. Worth reading the full Dev.to post if you mix Firebase and React lists.

