Files
Redflag/docs/3_BACKLOG/P0-002_Session-Loop-Bug.md

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

  1. Complete setup wizard in fresh installation
  2. Click "Restart Server" button (or restart manually via docker-compose restart server)
  3. Server goes down, Docker components restart
  4. UI automatically redirects from setup to dashboard
  5. BUG: Screen starts flashing/rapid refresh loop
  6. Clicking Logout stops the loop
  7. 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:

  1. Initial render: creates interval 1
  2. Server goes down: can't fetch health, sets wasInSetupMode
  3. Effect re-runs: interval 1 still running, creates interval 2
  4. Server comes back: detects not in setup mode
  5. Effect re-runs again: interval 1 & 2 still running, creates interval 3
  6. 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

  1. 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
    
  2. 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)
    
  3. 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
  4. 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