{"record":{"id":"1c60352ed7de9a2a","repo":"dotnet/runtime","slug":"module-ready-couldn-t-be-redefined","errorCode":null,"errorMessage":"Module.ready couldn't be redefined.","messagePattern":"Module\\.ready couldn't be redefined\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mono/browser/runtime/loader/run.ts","lineNumber":301,"sourceCode":"}\n\nlet emscriptenPrepared = false;\nasync function prepareEmscripten (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) {\n    if (emscriptenPrepared) {\n        return;\n    }\n    emscriptenPrepared = true;\n    if (ENVIRONMENT_IS_WEB && loaderHelpers.config.forwardConsole && typeof globalThis.WebSocket != \"undefined\") {\n        setup_proxy_console(\"main\", globalThis.console, globalThis.location.origin);\n    }\n    mono_assert(emscriptenModule, \"Null moduleConfig\");\n    mono_assert(loaderHelpers.config, \"Null moduleConfig.config\");\n\n    // extract ModuleConfig\n    if (typeof moduleFactory === \"function\") {\n        const extension = moduleFactory(globalObjectsRoot.api) as any;\n        if (extension.ready) {\n            throw new Error(\"Module.ready couldn't be redefined.\");\n        }\n        Object.assign(emscriptenModule, extension);\n        deep_merge_module(emscriptenModule, extension);\n    } else if (typeof moduleFactory === \"object\") {\n        deep_merge_module(emscriptenModule, moduleFactory);\n    } else {\n        throw new Error(\"Can't use moduleFactory callback of createDotnetRuntime function.\");\n    }\n\n    await detect_features_and_polyfill(emscriptenModule);\n}\n\nexport async function createEmscripten (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)): Promise<RuntimeAPI | EmscriptenModuleInternal> {\n    await prepareEmscripten(moduleFactory);\n\n    if (BuildConfiguration === \"Debug\" && !ENVIRONMENT_IS_WORKER) {\n        mono_log_info(`starting script ${loaderHelpers.scriptUrl}`);\n        mono_log_info(`starting in ${loaderHelpers.scriptDirectory}`);","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/src/mono/browser/runtime/loader/run.ts#L283-L319","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove the 'ready' property from the object returned by your moduleFactory function.","If you need to know when the runtime is ready, await the promise returned by create() or run() instead of using Module.ready.","Switch from passing a function to passing a plain config object (DotnetModuleConfig) to withModuleConfig(), avoiding the factory pattern entirely.","If you need custom Emscripten module properties, set everything EXCEPT ready, and use the runtime's own ready promise."],"exampleFix":"// before\nconst api = await createDotnetRuntime((api) => ({\n  ready: new Promise(resolve => { /* ... */ }),\n  print: console.log,\n}));\n\n// after (remove 'ready', use the returned promise)\nconst api = await createDotnetRuntime((api) => ({\n  print: console.log,\n}));","handlingStrategy":"validation","validationCode":"// Validate moduleFactory return value before passing\ndelete moduleConfig.ready; // strip any 'ready' property\nif (typeof moduleConfig === 'function') {\n  const test = moduleConfig(api);\n  if (test && 'ready' in test) {\n    throw new Error('Do not set ready on module config; use the runtime ready promise instead.');\n  }\n}","typeGuard":"function isCleanModuleConfig(config: any): boolean {\n  return config !== null && typeof config === 'object' && !('ready' in config);\n}","tryCatchPattern":null,"preventionTips":["Never include a 'ready' property in your Emscripten module config for the .NET runtime.","Prefer the high-level dotnet.create() API which handles readiness internally.","If migrating from raw Emscripten, strip Emscripten-specific lifecycle hooks like 'ready', 'onRuntimeInitialized', etc."],"tags":["configuration","emscripten","startup","api-misuse"],"backgroundTag":null,"analyzedSha":"60108ba66eb7d1d12f595480091b4ad80a24b172","analyzedAt":"2026-08-10T18:54:11.478Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}