Day 6: View Safety and Determinism

views indexeddb defensive-programming debugging

A cryptic error: "Failed to execute 'bound' on 'IDBKeyRange': The parameter is not a valid key." This is the story of how we found, understood, and permanently fixed a bug that could have broken every View in the system.

The Bug

Views were crashing randomly. Not consistently—some worked, some didn't. The error message was IndexedDB's way of saying "you gave me garbage".

We traced it to Dexie's where() and between() methods. These translate to IDBKeyRange.bound() internally. When you pass invalid values—undefined, NaN, Infinity, or the wrong types—IndexedDB explodes.

Root Cause Analysis

The bug had multiple potential entry points:

// These are all invalid IndexedDB keys:
undefined          // Not a key at all
NaN                // Invalid number
Infinity           // Invalid number
true / false       // Booleans aren't keys
{ object: 1 } // Objects aren't keys
[undefined, 123]   // Array with invalid element

The core issue: we trusted filter values. Users could create Views with any filter values, and those values went straight to IndexedDB without validation.

The Fix: Defensive Validation

We built a multi-layer defense system:

Layer 1: View Validator

A new module (view-validator.ts) that validates every filter value BEFORE it touches IndexedDB. Invalid values are caught early with clear error messages.

Layer 2: Value Normalization

Filter values are normalized by field type. Dates become timestamps. Amounts stay integers. IDs stay strings. No implicit type coercion at query time.

Layer 3: Repository Guards

Every repository method that uses indexed queries now validates its parameters. No undefined workspaceIds, no NaN timestamps.

Layer 4: Error Context

When validation fails, we capture the exact filter that failed, the value it received, and why it's invalid. No more cryptic IndexedDB errors.

The Validation Rules

We defined explicit rules for each filter type:

// Field Type → Expected Value
date          → number (timestamp, not NaN, finite)
amount        → number (integer, not NaN, finite)
accountId     → string (non-empty)
categoryId    → string | null
type          → string ('income'|'expense'|'transfer')
reconciledAt  → number | null (timestamp or null)
payee         → string
description   → string

// Operator-specific validation
between   → [min, max] both defined, min <= max
in/nin    → non-empty array of valid primitives
isNull    → no value validation needed

User-Facing Errors

Invalid filters now produce clear messages:

"Filter 1 (date between): start value (min) cannot be undefined or null"

"Filter 2 (accountId in): 'in' operator requires a non-empty array"

"Configuration error: Workspace ID is required but was undefined"

No more guessing what went wrong. Users can fix their filters immediately.

Why This Matters for AI and Sharing

This hardening isn't just about fixing bugs—it's about trust:

  • AI-generated Views — When AI creates Views, they must be validated. An AI can't crash the system with bad filters.
  • Imported Views — Views shared via links or templates must be safe to execute. Validation ensures they can't corrupt local data.
  • Deterministic Results — Same View definition = same results. Invalid values can't cause non-deterministic failures.

Execution Logging

For debugging, the QueryEngine now logs execution context (local-only, no telemetry):

// Success
[QueryEngine] Execution completed: {
  viewId: "abc123",
  viewName: "Spending by Category",
  duration: 45,
  transactionsLoaded: 150,
  transactionsFiltered: 87
}

// Failure
[QueryEngine] View execution failed: {
  viewId: "xyz789",
  filterErrors: [{
    filterIndex: 0,
    field: "date",
    operator: "between",
    message: "start value must be a valid number"
  }]
}

Test Coverage

We added regression tests covering:

  • All built-in Views execute without error
  • Undefined/null workspaceId is rejected
  • NaN/Infinity filter values are rejected
  • Between operator validates both endpoints
  • In/nin operators require non-empty arrays
  • Invalid filters fail gracefully with clear messages

Key Takeaway

Never trust user input at the database layer. Dexie doesn't validate for you. IndexedDB doesn't validate for you. Validation must happen before queries are built.

This fix ensures that Views are reliable, deterministic, and safe—the foundation we need before building the Rules Engine.

— The Accelerate Finance Team