dotnet/runtime · error · Error

cwrap ${name} not found or not a function

Error message

cwrap ${name} not found or not a function

What it means

Thrown by the internal `cwrap` helper in the .NET wasm runtime when it cannot resolve a C export by name. The helper first tries a fast path reading `Module.wasmExports[name]`, then falls back to emscripten's `Module.cwrap`; if neither yields a function, the runtime cannot expose the native interop binding and aborts. This almost always indicates the wasm module was stripped of an expected export or `init_c_exports` ran before the wasm module finished instantiating.

Source

Thrown at src/mono/browser/runtime/cwraps.ts:308

            // Module["wasmExports"] may not be defined yet if we are early enough in the startup process
            //  in that case, we need to rely on emscripten's lazy wrappers
            Module["wasmExports"]
            ? <Function>((<any>Module["wasmExports"])[name])
            : undefined;

    // If the argument count for the wasm function doesn't match the signature, fall back to cwrap
    if (fce && argTypes && (fce.length !== argTypes.length)) {
        mono_log_error(`argument count mismatch for cwrap ${name}`);
        fce = undefined;
    }

    // We either failed to find the raw wasm func or for some reason we can't use it directly
    if (typeof (fce) !== "function")
        fce = Module.cwrap(name, returnType, argTypes, opts);

    if (typeof (fce) !== "function") {
        const msg = `cwrap ${name} not found or not a function`;
        throw new Error(msg);
    }
    return fce;
}

export function init_c_exports (): void {
    const fns = [...fn_signatures];
    for (const sig of fns) {
        const wf: any = wrapped_c_functions;
        const [lazyOrSkip, name, returnType, argTypes, opts] = sig;
        const maybeSkip = typeof lazyOrSkip === "function";
        if (lazyOrSkip === true || maybeSkip) {
            // lazy init on first run
            wf[name] = function (...args: any[]) {
                const isNotSkipped = !maybeSkip || !lazyOrSkip();
                mono_assert(isNotSkipped, () => `cwrap ${name} should not be called when binding was skipped`);
                const fce = cwrap(name, returnType, argTypes, opts);
                wf[name] = fce;
                return fce(...args);

View on GitHub (pinned to 60108ba66e)

Solutions

  1. Re-deploy the matching pair of `dotnet.runtime.js` and `dotnet.native.wasm` from one publish output.
  2. Defer any runtime API calls until after `dotnet.createDotnetRuntime` resolves (the `onRuntimeInitialized` / module.ready promise).
  3. If you customised emscripten export flags, ensure `-s EXPORTED_FUNCTIONS` (or `EXPORT_KEEPALIVE`) includes the missing native symbol; check the name printed in the error.
  4. Rebuild with `--verbosity detailed` and inspect the linker output to confirm the export survives.

Example fix

// before: calling a binding before runtime ready
const r = cwraps.mono_wasm_add_assembly(...); // throws 'cwrap mono_wasm_add_assembly not found'

// after: wait for the runtime module promise
await dotnet.createDotnetRuntime(opts);
const r = cwraps.mono_wasm_add_assembly(...);
Defensive patterns

Strategy: validation

Validate before calling

function isExportAvailable (name) {
  const m = Module.wasmExports || {};
  return typeof m[name] === 'function';
}
// guard before use:
if (!isExportAvailable('mono_wasm_add_assembly')) { /* await runtimeReadyPromise */ }

Type guard

function isWasmReady (): boolean {
  return !!Module.wasmExports && typeof Module.cwrap === 'function';
}

Try / catch

try {
  return cwraps.mono_wasm_add_assembly(name, ptr, len);
} catch (e) {
  if (e instanceof Error && /cwrap .* not found/.test(e.message)) {
    throw new Error(`Runtime binding missing; ensure the runtime module finished loading and that dotnet.runtime.js matches dotnet.native.wasm. Cause: ${e.message}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Triggered from `init_c_exports` (or its lazy per-call wrapper at cwraps.ts:321-327) when iterating `fn_signatures` and binding each name. A name is missing because (a) the linker/emscripten dead-code-elimination removed the export, (b) the loaded `dotnet.native.wasm` is from a different build than `dotnet.runtime.js`, or (c) `Module.wasmExports` is still undefined and emscripten's lazy wrapper also returns nothing.

Common situations: Using a custom emscripten link step that strips exports; partial deploy where `dotnet.native.wasm` and `dotnet.runtime.js` come from different SDK builds; calling a wrapped export very early in startup before `onRuntimeInitialized`; running against an AOT/interpreter wasm build that omitted a particular native export.

Related errors


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