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:
- Add setTimeout fallback (violates RxJS-only convention)
- Use defaultIfEmpty + switchMap to throw
- 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
libs/libs-ui/auth-ui/src/lib/services/desktop-auth-exchange.service.ts
🔗 Related
- Decision ID:
dev_1774890165464_ym4skmb - Session: 290
- Workspace: gspace1
Auto-generated by MCP Smart Consolidation