dotnet/runtime · error · Error

Module.ready couldn't be redefined.

Error message

Module.ready couldn't be redefined.

What it means

Thrown by prepareEmscripten() when a moduleFactory function is passed to createDotnetRuntime and the returned module extension object contains a truthy 'ready' property. The .NET runtime manages its own Module.ready lifecycle internally; allowing user code to override it would break startup synchronization. The guard at line 300 explicitly rejects this to prevent a silent deadlock or corrupt startup state.

Solutions

  1. Remove the 'ready' property from the object returned by your moduleFactory function.
  2. If you need to know when the runtime is ready, await the promise returned by create() or run() instead of using Module.ready.
  3. Switch from passing a function to passing a plain config object (DotnetModuleConfig) to withModuleConfig(), avoiding the factory pattern entirely.
  4. If you need custom Emscripten module properties, set everything EXCEPT ready, and use the runtime's own ready promise.

Example fix

// before
const api = await createDotnetRuntime((api) => ({
  ready: new Promise(resolve => { /* ... */ }),
  print: console.log,
}));

// after (remove 'ready', use the returned promise)
const api = await createDotnetRuntime((api) => ({
  print: console.log,
}));
Defensive patterns

Strategy: validation

Validate before calling

// Validate moduleFactory return value before passing
delete moduleConfig.ready; // strip any 'ready' property
if (typeof moduleConfig === 'function') {
  const test = moduleConfig(api);
  if (test && 'ready' in test) {
    throw new Error('Do not set ready on module config; use the runtime ready promise instead.');
  }
}

Type guard

function isCleanModuleConfig(config: any): boolean {
  return config !== null && typeof config === 'object' && !('ready' in config);
}

Prevention

When it happens

Trigger: Passing a function to createDotnetRuntime() (or the moduleFactory argument of createEmscripten) whose return value includes a 'ready' callback or promise. This is an Emscripten-style Module pattern where developers sometimes set Module.ready themselves.

Common situations: Migrating from an older version of dotnet-wasm or from raw Emscripten where setting Module.ready was common. Following outdated tutorials that show a custom module factory with a ready callback. Copying Emscripten boilerplate into the .NET runtime config.

Related errors


AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10). Data as JSON: /api/errors/1c60352ed7de9a2a. Report an issue: GitHub.

Appendix: source

Thrown at src/mono/browser/runtime/loader/run.ts:301

}

let emscriptenPrepared = false;
async function prepareEmscripten (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) {
    if (emscriptenPrepared) {
        return;
    }
    emscriptenPrepared = true;
    if (ENVIRONMENT_IS_WEB && loaderHelpers.config.forwardConsole && typeof globalThis.WebSocket != "undefined") {
        setup_proxy_console("main", globalThis.console, globalThis.location.origin);
    }
    mono_assert(emscriptenModule, "Null moduleConfig");
    mono_assert(loaderHelpers.config, "Null moduleConfig.config");

    // extract ModuleConfig
    if (typeof moduleFactory === "function") {
        const extension = moduleFactory(globalObjectsRoot.api) as any;
        if (extension.ready) {
            throw new Error("Module.ready couldn't be redefined.");
        }
        Object.assign(emscriptenModule, extension);
        deep_merge_module(emscriptenModule, extension);
    } else if (typeof moduleFactory === "object") {
        deep_merge_module(emscriptenModule, moduleFactory);
    } else {
        throw new Error("Can't use moduleFactory callback of createDotnetRuntime function.");
    }

    await detect_features_and_polyfill(emscriptenModule);
}

export async function createEmscripten (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)): Promise<RuntimeAPI | EmscriptenModuleInternal> {
    await prepareEmscripten(moduleFactory);

    if (BuildConfiguration === "Debug" && !ENVIRONMENT_IS_WORKER) {
        mono_log_info(`starting script ${loaderHelpers.scriptUrl}`);
        mono_log_info(`starting in ${loaderHelpers.scriptDirectory}`);

View on GitHub (pinned to 60108ba66e)