electron: Renderer Node fs API stops working on page reload when allowRendererProcessReuse is true

Preflight Checklist

  • I have read the Contributing Guidelines for this project.
  • I agree to follow the Code of Conduct that this project adheres to.
  • I have searched the issue tracker for an issue that matches the one I want to file, without success.

Issue Details

  • Electron Version:
    • 8.0.0
  • Operating System:
    • Windows 10 (1809)
  • Last Known Working Electron version:
    • Unknown

When setting app.allowRendererProcessReuse = true and after reloading the renderer (Ctrl+R in developer console), Node fs APIs would randomly stop working. Specifically, the callbacks are never called, or if you’re using fs.promises, the promises are never resolved.

An interesting observation is: when you reload again, for a brief moment before the page is refreshed, the previous fs calls are magically unblocked. This can be observed by console.log lines suddenly printing the moment you hit Ctrl+R which disappears quickly as the whole page reloads.

Possibly related: https://github.com/electron/electron/issues/19554 This issue sounds like the opposite of what’s happening here.

Also note: issue does not present itself when app.allowRendererProcessReuse = false

Expected Behavior

From the repro steps, expecting to see local files printed in the console:

...
renderer.js:8 Reading file main.js
renderer.js:8 Reading file package-lock.json
renderer.js:8 Reading file package.json
renderer.js:8 Reading file preload.js
renderer.js:8 Reading file README.md
renderer.js:8 Reading file renderer.js
renderer.js:14 done

Actual Behavior

List is printed the first time the app loads. Hit Ctrl+R and observe only a few lines printed, or none at all. Hit Ctrl+R again and observe more lines printed for a split second before renderer reloads.

To Reproduce

Clone https://github.com/electron/electron-quick-start

rendereer.js

(async () => {
	const fs = require('fs').promises;

	for (let i = 0; i < 10; i++) {
		let list = await fs.readdir('.', {withFileTypes: true});
		for (let file of list) {
			if (file.isFile()) {
				console.log('Reading file', file.name);
				await fs.readFile(file.name, 'utf-8');
			}
		}
	}

	console.log('done');
})();

main.js

const {app, BrowserWindow} = require('electron');

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  });

  win.loadFile('index.html');
  win.webContents.openDevTools();
}

app.whenReady().then(createWindow);

app.allowRendererProcessReuse = true;
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

Screenshots

First boot: image On reload: image

Additional Information

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 35
  • Comments: 43 (20 by maintainers)

Commits related to this issue

Most upvoted comments

With the official release of v9.0.0, app.allowRendererProcessReuse is now set to true by default. Curious to know if the electron team is confident this issue isn’t going to happen frequently?

I have the feeling that with the default changed, we can anticipate a lot of electron apps run into this weird bug that would cause the developers hours of headache on why their fs promises/callbacks never resolve. I have personally spent over 5 hours before concluding the issue was allowRendererProcessReuse and coming up with the repro steps.

So… just want to give it a bump. I think it’s fair to be low priority while allowRendererProcessReuse was still off by default, but we can anticipate more trouble coming with the v9.0.0 release.

Thanks again for all this amazing work!

hey all, i believe this may be related to libuv handle closing logic. A better approach to our logic for this was recently merged in https://github.com/electron/electron/pull/25332 and repeated testing with this gist can no longer reproduce this issue on master, where this PR has landed. I’ve opened up some backports to 11-x-y, 10-x-y, and 9-x-y which may solve this problem.

As an update for all who run into this - we recognize this bug is a blocker for many and do not plan to remove allowRendererProcessReuse until it is resolved.

@sofianguy @codebytere Really sorry for the ping. Can someone please at least tag this issue with 9.x and 10.x?

@Seblor success! i was on macOS - it seems now to be more specific to Windows.

Additionally, this seems more specific to the async call - if i swap readFile for readFileSync I don’t see this issue

Haven’t seen this issue occur since Electron 10.1.4. Definitely has reduced if anything.

Thanks! Will keep digging

This appears to be a widespread issue that affects many apps. There are a lot of active issues on the same topic. Has there been any official response?

Just updated from electron 8 to 9. Spent 2 days trying to figure out why my https requests (using node’s https module) would never return after refreshing from the dev console. Surprisingly, doing some hacky setInterval( /* make a new https request to the same endpoit */ , 500) seemed to “wake up” the pending requests, but I never got this ironed out. Disabling allowRendererProcessReuse got the http requests working again.

I see electron 11 plans to Remove the ability to change app.allowRendererProcessReuse in Electron 11. I hope this is resolved prior to disabling this workaround.

I recently started updating all my project’s dependencies, and I found the same behavior.

When updating to electron 6.0.0 or newer, setting allowRendererProcessReuse at true breaks many calls to fs (e.g. readDir, access, mkdir, exists) after a reload : sync methods hang, and callbacks never resolve.

However, the issue #19554 never happens to me (my config is based on Electron-Vue: https://github.com/SimulatedGREG/electron-vue/blob/master/template/src/main/index.js)

Environment: Electron 6+, Windows 10 (1903), Node 12.16.1 (LTS)

@codebytere After many many sessions of debugging I found a repro (I’m on Windows 10):

  • Perform a long-running async node call (in my case, the call was fs.writeFile)
  • Reload the page before the call finishes.
  • Observe that any subsequent node calls never triggers the callback.
  • Reload again fixes the issue.

@codebytere, I still see the issue in the release 10.1.3

I also spent a super long time trying to find a solution to this bug. Thought I was going crazy, haha. Thanks for the app.allowRendererProcessReuse = false tip in here. I can confirm it works on my end. Is there a performance impact of setting this however? Also in the language on https://www.electronjs.org/docs/api/app about this property it seems like it will be gone in a future release as mentioned here. Does that mean that this issue will arise again in the future and there will be no way to set this to false?

PS - I’m also curious how this issue isn’t more widespread… I use my preload.js as a means to run Node APIs (which I thought was a best practice) and I imagine many others are too, so it’s just funny how hard it was to find a solution to it.

Funny, i had this same issue yesterday #24073 and it was closed prematuraly ;-; The only choice seems to set allowRendererProcessReuse to false if you use electron 9.0 or higher for now, but what will happen if this property gets deleted in the future? Doc says :

The intention is for these overrides to become disabled by default and then at some point in the future this property will be removed. (https://www.electronjs.org/docs/api/app)

For me, i think the callbacks are called to the previous renderer, so if i reload a page or go to another one the page is strangely still connected to the previous renderer process. I mean, there’s no error, everything is ignored in the callback function, so i don’t see any other explanation than that… right?

Looking forward to testing it. Thanks already for working on it @codebytere!

@KishanBagaria the issue was most prominent on Windows but this fix should ameliorate issues cross-platform! I’ll update the title actually.

@codebytere I used your exact files and still have the error when reloading the app.

I ran it on Windows 10 (2004), Node 13.8.0 and Electron 10.1.3.

Tell me if you need more info, and thanks for looking into this issue.

@ffflorian It works anywhere that has node apis. If nodeIntegration is enabled it can be placed anywhere in the render process. The preload file always works. If you are going to use it I suggest using the process.stdout.write version with the interval set to the maximum latency that is acceptable for your application to reduce cpu usage.

I’m probably going to move the stuff I was using fs for to the main process anyway, but is there any reason this shouldn’t work? Maybe some handles aren’t getting closed?

In my own code, if I have app.allowRendererProcessReuse = true;, I see the behavior described here. If I have app.allowRendererProcessReuse = false; I see the behavior described in #19554, though using setTimeout/setImmediate to call loadURL does not solve the issue.

Swapping to readdirSync and statSync seems to solve the issue, regardless of app.allowRendererProcessReuse setting.

On Win10, electron 8.0.2. Node 12.3.1.

EDIT: Upon further testing, using any promise based fs method locks the renderer. It also looks like electron 8.x uses Node 12.13.0; once I’m able I’ll test with a more up-to-date version of node.