Bugfix: How to prevent silent poll exhaustion from leaving the UI in...

Documentation for Sessions

🐛 Bugfix: How to prevent silent poll exhaustion from leaving the UI in...

Date: 2026-03-30
Time: 17:02:45 UTC
Session: gspace1-290.5
Agent: angular
Confidence: 96%
Status: ✅ Documented


📋 Decision Context

Question:

How to prevent silent poll exhaustion from leaving the UI in a permanent 'Waiting for browser...' state?

Task: S290: Fix silent poll exhaustion in DesktopAuthExchangeService


🎯 Decision Made

Chosen:

Added throwIfEmpty(() => new Error('Token exchange timed out')) after take(1) in the pollForToken() pipeline. This ensures the Observable errors (not silently completes) when all 60 poll attempts are exhausted without a successful HTTP 200 response.

Options Considered:

  1. Add setTimeout fallback (violates RxJS-only convention)
  2. Use defaultIfEmpty + switchMap to throw
  3. Use throwIfEmpty operator after take(1)

💭 Reasoning

ROOT CAUSE: The polling pipeline uses timer(0, 2000).pipe(take(60), switchMap(...), take(1)). When all 60 attempts return 404 (EMPTY), the timer completes, switchMap propagates the completion, and take(1) completes without emitting. In the race, poll$ completes silently while deepLink$ never completes (event listener). The merge doesn't complete, the timeout hasn't fired yet (timer vs poll are within 2s), and the UI is stuck. The fix is throwIfEmpty which converts the empty completion into an error, which propagates through merge to the error handler in startAuthRace(), showing the timeout message. Without this fix, users hitting any scenario where the POST fails (network blip, backend restart) would be permanently stuck on 'Waiting for browser...' with no error message and no recovery path.


📁 Files Involved


🔗 Related


Auto-generated by MCP Smart Consolidation