dotnet/runtime · error · Error

Failed to invoke 'onRuntimeReady' on library initializer

Error message

Failed to invoke 'onRuntimeReady' on library initializer '${name}': ${message}

What it means

Thrown by callLibraryInitializerOnRuntimeReady() (src/native/libs/Common/JavaScript/loader/assets.ts:86). After the runtime is ready, the loader invokes each module's onRuntimeReady(dotnetApi) hook; if the hook throws or rejects, the catch at line 93 wraps the original error and re-throws, tagging it with the initializer's name.

Solutions

  1. Read error.cause to find the underlying failure in the named initializer and fix it.
  2. Test the initializer against the exact runtime version it will run under.
  3. Register all JS exports the initializer needs before onRuntimeReady runs.

Example fix

// before: initializer calls an API that does not exist yet
export function onRuntimeReady(api) {
  api.setHeapLock(true); // TypeError -> 'Failed to invoke onRuntimeReady'
}

// after
export function onRuntimeReady(api) {
  if (typeof api.setHeapLock === 'function') api.setHeapLock(true);
}
Defensive patterns

Strategy: try-catch

Validate before calling

async function safeReadyHook(mod: any, api: any): Promise<void> {
  if (typeof mod?.onRuntimeReady === 'function') {
    await mod.onRuntimeReady(api);
  }
}

Type guard

function hasOnRuntimeReady(m: any): m is { onRuntimeReady: (api: any) => void | Promise<void> } {
  return typeof m?.onRuntimeReady === 'function';
}

Try / catch

try {
  await callLibraryInitializerOnRuntimeReady([asset, modulePromise]);
} catch (err) {
  // err.cause is the underlying initializer error
  console.error('onRuntimeReady failed:', (err as Error).message, (err as any).cause);
  throw err;
}

Prevention

When it happens

Trigger: A module listed in resources.modulesAfterRuntimeReady exports onRuntimeReady; the loader calls it with the runtime API; if it throws synchronously or returns a rejected promise, the error is wrapped and rethrown.

Common situations: A library initializer that uses dotnetApi incorrectly against the deployed runtime version; an initializer that fails to register a JS export the managed side expects; a bug in the onRuntimeReady body; a missing dependency the initializer needs at runtime-ready time.

Related errors


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

Appendix: source

Thrown at src/native/libs/Common/JavaScript/loader/assets.ts:95

            await module.onRuntimeConfigLoaded(loaderConfig);
        }
        return module;
    } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        throw new Error(`Failed to invoke 'onRuntimeConfigLoaded' on library initializer '${name}': ${message}`, { cause: err });
    }
}

export async function callLibraryInitializerOnRuntimeReady([asset, modulePromise]: [JsAsset, Promise<any>]): Promise<void> {
    const module = await modulePromise;
    const name = asset.name || asset.resolvedUrl || "unknown";
    try {
        if (typeof module.onRuntimeReady === "function") {
            await module.onRuntimeReady(dotnetApi);
        }
    } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        throw new Error(`Failed to invoke 'onRuntimeReady' on library initializer '${name}': ${message}`, { cause: err });
    }
}

export function fetchMainWasm(asset: WasmAsset): Promise<Response> {
    totalAssetsToDownload++;
    const assetInternal = asset as AssetEntryInternal;
    if (assetInternal.name && !asset.resolvedUrl) {
        asset.resolvedUrl = locateFile(assetInternal.name);
    }
    assetInternal.behavior = "dotnetwasm";
    if (!asset.resolvedUrl) throw new Error("Invalid config, resources is not set");
    mainWasmAsset = asset;
    wasmBinaryPromise = loadResource(assetInternal);
    return wasmBinaryPromise;
}

export async function instantiateMainWasm(imports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback): Promise<void> {
    const assetInternal = mainWasmAsset as AssetEntryInternal;

View on GitHub (pinned to 60108ba66e)