File watching does nothing when no window is focused

Author: davidjgraphCreated Sep 8, 2026Updated Sep 8, 2026

watchFile() only registers a watcher if some window happens to be focused at that moment, so external file change detection silently does nothing otherwise.

src/main/electron.js:

async function watchFile(filePath)
{
	await assertReadablePath(filePath);

	let win = BrowserWindow.getFocusedWindow();

	if (win)
	{
		fs.watchFile(filePath, (curr, prev) => {
			win.webContents.send('fileChanged', {...});
		});
	}
}

If getFocusedWindow() returns null the function returns without registering anything and the IPC still resolves successfully. On the renderer side EditorUi.watchFile sets this.watchedPath = newPath before awaiting watchPath(), so the path looks watched and is never retried. Nothing re-arms on focus.

Verified on the 31.4.5 build (macOS, Electron 44.2.0) by inspecting the main process:

  • getFocusedWindow() returns NULL whenever the app is not frontmost
  • no StatWatcher handles are registered, so no watcher exists at all
  • the renderer still reports watchedPath as set
  • force registering fs.watchFile from main makes the whole chain work, the main callback fires and the renderer handleFileChange runs

So the IPC path is fine, only the registration is skipped.

Two related problems in the same function:

  1. The watcher captures whichever window was focused at registration time, not the window that asked. With more than one window open, change events for a file opened in a background window are sent to the foreground window, whose preload has no listener for that path, so they are dropped.

  2. unwatchFile() calls fs.unwatchFile(filePath) with no listener argument, which removes every listener for that path. Two windows watching the same file means one closing stops watching for both.

The dispatcher already has the sender:

ipcMain.on("rendererReq", async (event, args) =>

so watchFile can take event.sender and use that instead of getFocusedWindow(). That fixes the null case and the misrouting together. Passing the specific listener to fs.unwatchFile would fix the third.

Not a regression. The getFocusedWindow() pattern dates back to 031b2bb. The only recent change here was the assertReadablePath guard in 9c93351, which behaves correctly.