0

While I was able to get my C# code running in the browser as WebAssembly, I get errors like this when trying to run any (vitest) unit tests that load the module that initializes .NET:

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
TypeError: The URL must be of scheme file
 ❯ Object.fileURLToPath node:internal/url:1393:11
 ❯ src/_framework/dotnet.native.js:8:1475

 ❯ processTicksAndRejections node:internal/process/task_queues:95:5

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: 'ERR_INVALID_URL_SCHEME' }
This error originated in "src/[...].test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.

Based on console logs, it is triggered by the standard initialization statement:

import { dotnet } from '_framework/dotnet.js';
...
setTimeout(async () => {
    console.log('*** Initializing .NET');
    try {
        const { setModuleImports, getAssemblyExports, getConfig, runMain } 
            = await dotnet.withApplicationArguments('start').create();
    ...

It's important to also be able to use C# code in unit tests, so how do I fix this?

Note: I have a catch block which should have caught any exceptions, but my console.error does not appear. And the code that causes the error is minified so I can't read it.

1 Answer 1

1

Here's a module to fix the problem (see explanation in comment at the top):

import Module from 'module';
import { fileURLToPath as originalFileURLToPath } from 'url';

// Vitest setup file that fixes ERR_INVALID_URL_SCHEME in Microsoft's 
// dotnet.native.js  module, to enable .NET WebAssembly in Node.js. 
// The issue: dotnet.js tries to call fileURLToPath() on import.meta.url,
// but in vitest, import.meta.url can be an http:// URL from the dev 
// server. (or so GPT5 tells me)
//
// e.g. fileURLToPath may be called with http://localhost:3000/src/_framework
//      and this code converts that to C:\Dev\[Project]\UI\src\_framework
const originalRequire = Module.prototype.require;

(Module.prototype as any).require = function(this: any, id: string) {
    const module = originalRequire.apply(this, arguments as any);
    
    if (id === 'url') {
        console.log('🔧 INTERCEPTED: url module require()');
        // Return a patched version of the url module
        return new Proxy(module, {
            get(target: any, prop: string) {
                if (prop === 'fileURLToPath') {
                    return function(urlArg: string | URL): string {
                        const urlString = typeof urlArg === 'string' ? urlArg : urlArg.href;
                        console.log('🔧 Patched fileURLToPath called with:', urlString);
                        
                        // If it's an HTTP URL from vitest dev server, convert it to a file path
                        if (urlString.startsWith('http://') || urlString.startsWith('https://')) {
                            // Vitest typically serves from the project root.
                            // Convert something like http://localhost:3000/src/_framework
                            // to [working directory]\src\_framework
                            const urlObj = new URL(urlString);
                            let path = urlObj.pathname; // e.g. "/src/_framework"
                            
                            // Assume we're in the parent directory of src
                            const projectRoot = process.cwd();
                            path = projectRoot + path;
                            
                            console.log('  → Converted to:', path);
                            return path;
                        }
                        
                        return originalFileURLToPath(urlArg);
                    };
                }
                return target[prop];
            }
        });
    }
    
    return module;
};

Then configure vitest to reference it:

export default defineConfig({
    ...
    // Vitest configuration: https://vitest.dev/config/
    test: {
        ...
        // Setup file to patch Node.js URL handling for .NET WebAssembly runtime
        setupFiles: ['./test/fix-csharp-in-tests.ts'],
    },
});

That should fix it. Remove console.log when you get it to work. Fun facts:

  • don't call it fix-c#.ts as the # blocks it from loading.
  • After I got my first C# test working, I still saw a red output "+ CategoryInfo : NotSpecified: (11:21:14 AM...work/dotnet.js.:String) [], RemoteException". No idea what it means.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.