Search code examples
javascriptelectronprogressive-web-appscreate-directorynative-file-system-api-js

Fileystem API showDirectoryPicker() permanently broken if user doesn't choose a directory in Electron


This issue occurs in the latest version of Electron, but not in Chromium outside of Electron.

The following example code uses the Filesystem API's showDirectoryPicker method to open a directory-picker when a user clicks a button:

async function handleClick() {
    try {
        dirHandle = await window.showDirectoryPicker({startIn: "downloads"});
    } catch (error) {
        console.log(error);
    }
}

However the following circumstances lead to the directory-picker being permanently broken:

  • User is inside Downloads already, so doesn't think they need to create/choose a subdirectory
  • They click Open without making an explicit choice
  • The directory-picker "closes" with no directory chosen, and dirHandle is null.
  • The user clicks the button to try again
  • This call to showDirectoryPicker() throws an AbortError of NotAllowedError: Failed to execute 'showDirectoryPicker' on 'Window': File picker already active.

Any subsequent clicks to open the directory-picker fail with this same NotAllowedError: Failed to execute 'showDirectoryPicker' on 'Window': File picker already active. error.

So my question is: How can I deactivate, reset, close, delete, resolve, etc., the File Picker, so that it can be opened a second time. I can't make the dirHandle null since it's already null. Likewise, the Promise returned by showDirectoryPicker() is already resolved.

Any solution would be very welcome. There has to be some obvious way to resolve this, because otherwise it would be a massive bug in the Filesystem API.

Alternatively, is there any way to make showDirectoryPicker() just actually pick that starting directory (Downloads in this case)?

Electron vs Chromium

This only appears to be happening in Electron. In normal Chromium, this issue cannot occur because instead of giving the user an Open option, it gives them a Select option, and won't allow them not to select a directory.


Solution

  • This turned out to be an Electron-specific Issue. Luckily, one of the Electron engineers has pushed a fix that allows you to catch if a user tries to select a protected System directory. Better yet, the Electron implementation now lets the developer choose to offer the user the option to override the restriction and specify one of these directories, such as Desktop, Documents, Downloads, etc.

    The fix is documented in the Electron Documentation.

    In short, you just need to make sure to include dialog and session from electron at the top of main.js:

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

    and then add code like the following into your createWindow() function:

    session.defaultSession.on('file-system-access-restricted', async (e, details, callback) => {
        const { origin, path } = details
        const { response } = await dialog.showMessageBox({
            message: `Are you sure you want ${origin} to open restricted path ${path}?`,
            title: 'File System Access Restricted',
            buttons: ['Choose a different folder', 'Allow', 'Cancel'],
            cancelId: 2
        })
    
        if (response === 0) {
            callback('tryAgain')
        } else if (response === 1) {
            callback('allow')
        } else {
            callback('deny')
        }
    })
    

    This will prompt the user with a dialog box if they try and pick a protected directory, and give them the option to "Choose a different folder", "Allow" using that directory, or "Cancel". Even if they click "Cancel", they'll still be able to use the directory-picker again, unlike before.