Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get ERR_UNSUPPORTED_ESM_URL_SCHEME or ENOENT or ERR_INVALID_URL_SCHEME when use absolute path

When I run my code on Linux it works fine. But on Windows with the same node js version(18.18.0) I get errors.

I use modern ES imports and my package.json has string "type": "module".

I write discord bot and that is what I do:

const rootDir = dirname(fileURLToPath(import.meta.url));
const commandsPath = join(rootDir, 'src', 'commands');
console.log('BEFORE FILTER');
const commandFiles = readdirSync(commandsPath).filter(file => file.endsWith('.js'));

console.log('BEFORE LOOP');
for (const file of commandFiles) {
    console.log('BEFORE JOIN');
    const filePath = join(commandsPath, file);
    console.log('BEFORE AWAIT');
    const command = await import(filePath);
    console.log('SUCCESS');
    // etc...

So, if I run this code I get the logs BEFORE FILTER, BEFORE LOOP, BEFORE JOIN, BEFORE AWAIT and error

Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'c:'

I tried to change commandsPath initialization:

const commandsPath = join('file:', rootDir, 'src', 'commands');

Runs with the log BEFORE FILTER and ENOENT error with the correct path to commands directory

Error: ENOENT: no such file or directory, scandir 'file:\C:\Users\Correct\Path\To\src\commands'

If I try

const commandsPath = fileURLToPath(join('file:', rootDir, 'src', 'commands'));

BEFORE FILTER, BEFORE LOOP, BEFORE JOIN, BEFORE AWAIT and the first example ERR_UNSUPPORTED_ESM_URL_SCHEME error

And finally, if I try

const commandsPath = fileURLToPath(join('file:', rootDir, 'src', 'commands'));

and

const command = await import(fileURLToPath(filePath));

BEFORE FILTER, BEFORE LOOP, BEFORE JOIN, BEFORE AWAIT and TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file

What should I do?

like image 509
upcast Avatar asked Oct 25 '25 07:10

upcast


1 Answers

Apologize ahead, I can't test it since I don't run on Windows, I give an answer from knowledge, hope I have it right.

To solve the issue, you need to fix the URL from 'file:\C:\Users\Correct\Path\To\src\commands'.
To: file:///C:/Users/Correct/Path/To/src/commands.

There are two differences, one is 3 / instead of 1, the second is / instead of \.
This is a very stupid behavior of the API, but it is what it is.

To solve it, add \\, and as you did, use pathToFileURL to replace / by \.
If you want to use URL(path) to have a full object, use URL(path).href to get the serialized url.
At your original try:


const rootDir = dirname(fileURLToPath(import.meta.url));
const commandsPath = join(rootDir, 'src', 'commands');
console.log('BEFORE FILTER');
const commandFiles = readdirSync(commandsPath).filter(file => file.endsWith('.js'));

console.log('BEFORE LOOP');
for (const file of commandFiles) {
    console.log('BEFORE JOIN');
    const filePath = join(commandsPath, file);
    console.log('BEFORE URL CONVERSION');
    const fileURL = new pathToFileURL(`file://${filePath}`); // <- ** here **
    console.log('BEFORE AWAIT');
    const command = await import(fileURL);
    console.log('SUCCESS');
    // ... rest of your code
}

This try const commandsPath = join('file:', rootDir, 'src', 'commands'); should be const commandsPath = pathToFileURL(join('file:\\', rootDir, 'src', 'commands'));.

Lastly, if you don't care having extra package, you can use the simplest option:

npm install file-url
import fileUrl from 'file-url';
const commandsPath = fileUrl('src/commands');

console.log('BEFORE FILTER');
const commandFiles = readdirSync(commandsPath).filter(file => file.endsWith('.js'));

console.log('BEFORE LOOP');
for (const file of commandFiles) {
    console.log('BEFORE...');
    const command = await import(fileUrl(file));
    console.log('SUCCESS');
    // etc...

fileUrl has functionality of resolving path by itself. I'm not aware of the level of capabilities, but using the fileUrl cli tool I was able to get path from different locations in my machine using name only.

fileUrl npm

like image 150
avifen Avatar answered Oct 26 '25 21:10

avifen