Bugfix: How should SyncEngine handle non-404 HTTP errors (401, 403, ...

Documentation for Sessions

🐛 Bugfix: How should SyncEngine handle non-404 HTTP errors (401, 403, ...

Date: 2026-03-26
Time: 18:11:45 UTC
Session: gspace1-273.2
Agent: storage
Confidence: 95%
Status: ✅ Documented


📋 Decision Context

Question:

How should SyncEngine handle non-404 HTTP errors (401, 403, 500+) when fetching universes during background sync, given that only 404 was tracked — causing infinite retry loops for forbidden universes every 5 seconds?

Task: S273: Fix SyncEngine infinite 403 retry loop causing console spam


🎯 Decision Made

Chosen:

Three-tier error classification: (1) 404/401/403 = permanent failures → add to deletedUniverseIds set, remove from space.universeIds, clean local storage. (2) Transient errors (500, network, timeout) → track per-universe failure count in universeFailureCounts Map, skip after MAX_UNIVERSE_RETRIES=3 until app restart. (3) No change to existing push-side retry logic (already had retryAttempts cap).

Options Considered:

  1. Only track 404, leave 403 to retry (status quo minus the bug)
  2. Track 401/403 with 5-minute TTL expiry for periodic re-check
  3. Track 401/403 permanently like 404, plus cap transient retries at 3
  4. Add exponential backoff per-universe for ALL error types (no permanent block)

💭 Reasoning

Root cause: SyncEngine.pullSpaceFromBackend() iterates space.universeIds every 5 seconds. The catch block only handled status===404 by adding to deletedUniverseIds. All other errors (including 403) fell through to a generic logger.error() with NO tracking — meaning the same forbidden universe was retried every single sync cycle indefinitely. This produced the console spam visible in production screenshots.

Why 401/403 should be permanent like 404: (a) 403 means the authenticated user lacks permission to this universe — this won't change between sync cycles. (b) 401 with valid session means the token doesn't authorize this resource. (c) The user was confirmed authenticated (isAuthenticated: true in screenshots). (d) These universe IDs are likely orphaned from space transfers, sharing revocations, or soft-deletes.

Why NOT to add TTL-based re-check for 403: The cost/benefit is poor. If permission genuinely changes (rare), the user can reload the page to reset the in-memory set. The cost of NOT tracking is massive console spam every 5 seconds for every forbidden universe.

Why transient errors get capped retries (not permanent block): 500 errors and network timeouts ARE recoverable. But without a cap, a backend outage would cause the same infinite loop. 3 retries per universe per app session is a safe cap — enough to survive brief hiccups without enabling infinite loops.

What would make this decision wrong: If the platform implemented a "re-share" flow where a universe's permissions change mid-session and the user expects instant access without reload. Mitigation: the deletedUniverseIds set is in-memory only — a page reload clears it.


📁 Files Involved


🔗 Related


Auto-generated by MCP Smart Consolidation