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:
- Only track 404, leave 403 to retry (status quo minus the bug)
- Track 401/403 with 5-minute TTL expiry for periodic re-check
- Track 401/403 permanently like 404, plus cap transient retries at 3
- 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
libs/libs-foundational/core-angular/src/lib/services/sync-engine.service.ts
🔗 Related
- Decision ID:
dev_1774548705083_1tmfn0e - Session: 273
- Workspace: gspace1
Auto-generated by MCP Smart Consolidation