5.4 KiB
P0-002: Session Loop Bug (Returned)
Priority: P0 (Critical)
Source Reference: From SessionLoopBug.md line 4
Date Identified: 2025-11-12
Previous Fix Attempt: Commit 7b77641 - "fix: resolve 401 session refresh loop"
Problem Description
The session refresh loop bug has returned. After completing setup and the server restarts, the UI flashes/loops rapidly if you're on the dashboard, agents, or settings pages. Users must manually logout and log back in to stop the loop.
Reproduction Steps
- Complete setup wizard in fresh installation
- Click "Restart Server" button (or restart manually via
docker-compose restart server) - Server goes down, Docker components restart
- UI automatically redirects from setup to dashboard
- BUG: Screen starts flashing/rapid refresh loop
- Clicking Logout stops the loop
- Logging back in works fine
Root Cause Analysis
The issue is in the SetupCompletionChecker component which has a dependency array problem in its useEffect hook:
useEffect(() => {
const checkSetupStatus = async () => { ... }
checkSetupStatus();
const interval = setInterval(checkSetupStatus, 3000);
return () => clearInterval(interval);
}, [wasInSetupMode, location.pathname, navigate]); // ← Problem here
Issue: wasInSetupMode is in the dependency array. When it changes from false to true to false, it triggers new effect runs, creating multiple overlapping intervals without properly cleaning up the old ones.
During Docker Restart Sequence:
- Initial render: creates interval 1
- Server goes down: can't fetch health, sets
wasInSetupMode - Effect re-runs: interval 1 still running, creates interval 2
- Server comes back: detects not in setup mode
- Effect re-runs again: interval 1 & 2 still running, creates interval 3
- Result: 3+ intervals all polling every 3 seconds = rapid flashing
Proposed Solution
Option 1: Remove wasInSetupMode from dependencies (Recommended)
useEffect(() => {
let wasInSetup = false;
const checkSetupStatus = async () => {
// Use local wasInSetup variable instead of state dependency
// ... existing logic using wasInSetup local variable
};
checkSetupStatus();
const interval = setInterval(checkSetupStatus, 3000);
return () => clearInterval(interval);
}, [location.pathname, navigate]); // Only pathname and navigate
Option 2: Add interval guard
const [intervalId, setIntervalId] = useState<number | null>(null);
useEffect(() => {
// Clear any existing interval first
if (intervalId) {
clearInterval(intervalId);
}
const checkSetupStatus = async () => { ... };
checkSetupStatus();
const newInterval = setInterval(checkSetupStatus, 3000);
setIntervalId(newInterval);
return () => clearInterval(newInterval);
}, [wasInSetupMode, location.pathname, navigate]);
Definition of Done
- No screen flashing/looping after server restart
- Single polling interval active at any time
- Clean redirect to login page after setup completion
- No memory leaks from uncleared intervals
- Setup completion checker continues to work normally
Test Plan
-
Fresh Setup Test:
# Clean start docker-compose down -v --remove-orphans rm config/.env docker-compose build --no-cache cp config/.env.bootstrap.example config/.env docker-compose up -d # Complete setup wizard through UI # Verify dashboard loads normally -
Server Restart Test:
# Restart server manually docker-compose restart server # Watch browser for: # - No multiple "checking setup status" logs # - No 401 errors spamming console # - No rapid API calls to /health endpoint # - Clean behavior (either stays on page or redirects properly) -
Console Monitoring Test:
- Open browser developer tools before server restart
- Watch console for multiple interval creation logs
- Monitor Network tab for duplicate /health requests
- Verify only one active polling interval after restart
-
Memory Leak Test:
- Open browser task manager (Shift+Esc)
- Monitor memory usage during multiple server restarts
- Verify memory doesn't grow continuously (indicating uncleared intervals)
Files to Modify
aggregator-web/src/components/SetupCompletionChecker.tsx(main component)- Potentially related:
aggregator-web/src/lib/store.ts(auth store) - Potentially related:
aggregator-web/src/pages/Setup.tsx(calls logout before configure)
Impact
- Critical User Experience: UI becomes unusable after normal server operations
- Production Impact: Server maintenance/restarts break user experience
- Perceived Reliability: System appears broken/unstable
- Support Burden: Users require manual intervention (logout/login) after server restarts
Technical Notes
- This bug only manifests during server restart after setup completion
- Previous fix (commit
7b77641) addressed 401 loop but didn't solve interval cleanup - The issue is specific to React effect dependency management
- Multiple overlapping intervals cause the rapid flashing behavior
Verification Commands
After implementing fix:
# Monitor browser console during restart
# Should see only ONE "checking setup status" log every 3 seconds
# Test multiple restarts in succession
docker-compose restart server
# Wait 10 seconds
docker-compose restart server
# Wait 10 seconds
docker-compose restart server
# UI should remain stable throughout