# Execution Plan: Fix Scheduler Concurrency and Browser Stability ## Context The application is experiencing two related issues with the Instagram authentication scheduler: 1. **Console Spam**: "Auth renewal already in progress" is logged repeatedly. This indicates the scheduler is triggering new renewal attempts while a previous one is still active (or perceived as active). This is likely caused by an invalid or extremely short interval configuration (e.g., `NaN` resulting from parsing failure). 2. **Browser Instability**: "Target page, context or browser has been closed" errors. This occurs when the scheduler attempts to use a cached Playwright browser instance that has crashed or disconnected. ## Goal Ensure the authentication scheduler runs reliably at the configured interval without overlapping executions, and make the browser instance management robust against crashes. ## Exception to workflow Do not create a dedicated branch. It's a fix on a new feature. ## Proposed Solution ### Story 1: Fix Scheduler Configuration and Resource Cleanup **Objective**: Prevent rapid-fire execution of the scheduler and ensure browser resources are cleaned up properly even when errors occur. **Changes**: 1. **Validate Configuration**: In `src/lib/server/scheduler.ts`, update `getConfig()` to strictly validate `intervalMinutes`. * Handle `NaN` (parsing errors). * Enforce a minimum interval (e.g., 15 minutes) to prevent spamming. * Default to 720 minutes if invalid. 2. **Improve Resource Management**: Refactor `renewInstagramAuth` to ensure `page` and `context` are closed in a `finally` block (or nested `try/finally`), preventing resource leaks if an error occurs during the renewal process. **Verification**: * Set `AUTH_SCHEDULER_INTERVAL_MINUTES` to an invalid value (e.g., "abc") and verify it defaults to 720. * Verify that `setInterval` is called with a valid duration. ### Story 2: Robust Browser Lifecycle Management **Objective**: Ensure the application automatically recovers from browser crashes by detecting disconnected instances. **Changes**: 1. **Check Connection Status**: In `src/lib/server/browser.ts`, update `getBrowser()` to check `browser.isConnected()`. 2. **Auto-Recovery**: If the cached browser instance is not connected: * Log a warning. * Attempt to close the dead instance (swallowing errors). * Re-initialize a new browser instance. **Verification**: * Simulate a browser crash (e.g., by manually killing the chrome process if possible, or mocking `isConnected` to return false). * Verify that the next call to `getBrowser()` creates a new instance instead of throwing. ## Implementation Details ### `src/lib/server/scheduler.ts` ```typescript function getConfig(): SchedulerConfig { const enabled = env.AUTH_SCHEDULER_ENABLED === 'true'; let intervalMinutes = parseInt(env.AUTH_SCHEDULER_INTERVAL_MINUTES || '720', 10); if (isNaN(intervalMinutes) || intervalMinutes < 15) { console.warn(`[Scheduler] Invalid or too short interval '${env.AUTH_SCHEDULER_INTERVAL_MINUTES}'. Defaulting to 720 minutes.`); intervalMinutes = 720; } return { enabled, intervalMinutes }; } // In renewInstagramAuth: let context = null; let page = null; try { // ... setup ... context = await browser.newContext(...); page = await context.newPage(); // ... logic ... } catch (e) { // ... error handling ... } finally { if (page) await page.close().catch(() => {}); if (context) await context.close().catch(() => {}); state.isRenewing = false; } ``` ### `src/lib/server/browser.ts` ```typescript export async function getBrowser(): Promise { if (!browser || !browser.isConnected()) { if (browser) { console.warn('Browser is disconnected. Re-initializing...'); try { await browser.close(); } catch (e) { /* ignore */ } } return initializeBrowser(); } return browser; } ```