responsively-org/responsively-app · error · Error
"mainWindow" is not defined
Error message
"mainWindow" is not defined
What it means
This is an internal defensive assertion thrown inside the 'ready-to-show' event handler in createWindow. After the async initInstance() call completes, the module-level mainWindow variable is checked; if it became null/undefined (e.g. the window was closed during initialization), the code throws to prevent calling methods on a dead BrowserWindow reference. It signals a race between app initialization and window lifetime rather than a user-facing failure.
Source
Thrown at desktop-app/src/main/main.ts:203
needsFocusFix = false;
triggeringProgrammaticBlur = true;
setTimeout(function () {
mainWindow!.blur();
mainWindow!.focus();
setTimeout(function () {
triggeringProgrammaticBlur = false;
}, 100);
}, 100);
}
});
mainWindow.on('ready-to-show', async () => {
if (!isAppInitiated) {
await initInstance();
setIsAppInitiated();
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
webPermissionHandlers.init();
if (process.env.START_MINIMIZED) {
mainWindow.minimize();
} else if (process.env.E2E_TEST === 'true' && process.env.E2E_HEADLESS === 'true') {
windowShownOnOpen = true;
} else if (process.env.E2E_TEST === 'true') {
mainWindow.showInactive();
windowShownOnOpen = true;
} else {
mainWindow.showInactive();
if (!windowShownOnOpen) {
windowShownOnOpen = true;
mainWindow.show();
} else {
mainWindow.showInactive();
}
}View on GitHub (pinned to e5623c5a70)
Solutions
- Guard the handler before awaiting: if (!mainWindow) return; at the top of the ready-to-show callback and after the await
- Make the throw a graceful early return instead, since throwing inside an async Electron event handler only produces an unhandled rejection
- Track window lifetime with a flag or use mainWindow.isDestroyed() rather than relying on the module-level variable
- In tests, ensure the window stays open until ready-to-show completes
Example fix
// before
await initInstance();
setIsAppInitiated();
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
// after
if (!mainWindow || mainWindow.isDestroyed()) return;
await initInstance();
if (!mainWindow || mainWindow.isDestroyed()) return;
setIsAppInitiated(); Defensive patterns
Strategy: type-guard
Validate before calling
const win = mainWindow; if (!win || win.isDestroyed()) return; // run before AND after the await
Type guard
function isLiveWindow(w: Electron.BrowserWindow | null): w is Electron.BrowserWindow {
return w !== null && !w.isDestroyed();
} Try / catch
mainWindow.on('ready-to-show', async () => {
try {
await initInstance();
} catch (err) {
console.error('initInstance failed', err);
return;
}
if (!isLiveWindow(mainWindow)) return; // window closed during init
// ... proceed
}); Prevention
- Capture the window reference into a local const before any await
- Check isDestroyed() after every await that touches webContents
- Avoid throwing inside Electron async event handlers; prefer early return and logging
- In E2E tests, keep the window alive until ready-to-show fires
When it happens
Trigger: The user (or the OS/test harness) closes the window while initInstance() is still awaiting during the 'ready-to-show' event, so the 'closed' handler sets mainWindow = null before the null check runs.
Common situations: Fast app quit on startup (user closes splash window immediately), E2E test harnesses tearing down the window mid-initialization, START_MINIMIZED flows combined with early quit, or any refactor that makes initInstance() async/slow.
Related errors
AI-assisted analysis of responsively-org/responsively-app@e5623c5a70 (2026-08-31).
Data as JSON: /api/errors/4b9e1a29d05bda6e.
Report an issue: GitHub.