DEV Community

The Guide Co.
The Guide Co.

Posted on

Post-Mortem: How Firebase RTDB Array Coercion Bricked My React Frontend

1. The Incident

I was building Friday OS—my self-hosted, local-first AI Personal OS. The stack uses Firebase Realtime Database (RTDB) for real-time state syncing. I hit "Delete" on one of the log entries in my dashboard. The backend returned a clean 200 OK and Firebase Console updated immediately, but my React frontend went completely white. The console output showed:
TypeError: items.map is not a function

2. Root Cause: The Non-Existent Array

Firebase RTDB is a JSON tree. Under the hood, it has no native array type. When you write an array:

const agents = ["orchestrator", "fitness", "financial"];
Enter fullscreen mode Exit fullscreen mode

Firebase stores it as an object with stringified integer keys:

{
  "0": "orchestrator",
  "1": "fitness",
  "2": "financial"
}
Enter fullscreen mode Exit fullscreen mode

If the keys are sequential integers starting at 0, the Firebase SDK automatically coerces the data back into a JavaScript array on read. But if you delete index 1 (fitness), the database state becomes:

{
  "0": "orchestrator",
  "2": "financial"
}
Enter fullscreen mode Exit fullscreen mode

Because the sequence is broken, the SDK stops coercing and returns a raw JavaScript object. React expects an array to call .map(), receives an object instead, throws a TypeError, and crashes the screen.

3. The Code Fix: Defensive State Normalization

To prevent this, you must normalize your state where the Firebase snapshot enters your application:

// Defensive check to guarantee a true array
onValue(agentsRef, (snapshot) => {
  const data = snapshot.val();
  if (!data) {
    setAgents([]);
  } else if (Array.isArray(data)) {
    setAgents(data);
  } else {
    // Sequence was broken, extract values from the coerced object
    setAgents(Object.values(data));
  }
});
Enter fullscreen mode Exit fullscreen mode

Top comments (0)