4.2 KiB
Session Loop Bug (Returned)
Issue 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. User must manually logout and log back in to stop the loop.
Previous fix: Commit 7b77641 - "fix: resolve 401 session refresh loop"
- Added logout() call in Setup.tsx before configuration
- Cleared auth state on 401 in store
- Disabled retries in API client
Current Behavior
Steps to reproduce:
- Complete setup wizard
- Click "Restart Server" button (or restart manually)
- 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
Suspected Cause
The SetupCompletionChecker component is polling every 3 seconds and has a dependency array issue:
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:
- 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
- Now 3+ intervals all polling every 3 seconds = rapid flashing
Potential Fix Options
Option 1: Remove wasInSetupMode from dependencies
useEffect(() => {
let wasInSetup = false;
const checkSetupStatus = async () => {
// ... 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]);
Option 3: Increase polling interval during transitions
const pollingInterval = wasInSetupMode ? 5000 : 3000; // Slower during transition
const interval = setInterval(checkSetupStatus, pollingInterval);
Option 4: Stop polling after successful redirect
if (wasInSetupMode && !currentSetupMode && location.pathname === '/setup') {
console.log('Setup completed - redirecting to login');
navigate('/login', { replace: true });
// Don't set up interval again
return;
}
Testing
After applying fix:
# 1. Fresh setup
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
# 2. Complete setup wizard
# 3. Restart server via UI or manually
docker-compose restart server
# 4. Watch browser console for:
# - Multiple "checking setup status" logs
# - 401 errors
# - Rapid API calls to /health endpoint
# 5. Expected: No flashing, clean redirect to login
Related Files
aggregator-web/src/components/SetupCompletionChecker.tsx- Main componentaggregator-web/src/lib/store.ts- Auth store with logout()aggregator-web/src/pages/Setup.tsx- Calls logout before configureaggregator-web/src/lib/api.ts- API retry logic
Notes
This bug only manifests during the server restart after setup completion, making it hard to reproduce without a full cycle. The previous fix (commit 7b77641) addressed the 401 loop but didn't fully solve the interval cleanup issue.