dotnet/runtime · critical · Error

Expected ${shortName} to be a function

Error message

Expected ${shortName} to be a function

What it means

Thrown by replace_linker_placeholders in exports-binding.ts after it rebuilt the indexToNameMap from the emcc-generated import stubs. For each real function in wasmImports it looks up the short name in the wasm import object and asserts that env[shortName] is still a function before swapping the emcc stub for the real implementation. The assertion fires only if the stub slot was overwritten with a non-function between discovery and replacement, which indicates a corrupted or hand-modified WebAssembly import object rather than a normal usage error.

Source

Thrown at src/mono/browser/runtime/exports-binding.ts:146

    for (const shortName in env) {
        const stub_fn = env[shortName] as Function;
        if (typeof stub_fn === "function" && stub_fn.toString().indexOf("runtime_idx") !== -1) {
            try {
                const { runtime_idx } = stub_fn();
                if (indexToNameMap[runtime_idx] !== undefined) throw new Error(`Duplicate runtime_idx ${runtime_idx}`);
                indexToNameMap[runtime_idx] = shortName;
            } catch {
                // no-action
            }
        }
    }

    for (const [idx, realFn] of wasmImports.entries()) {
        const shortName = indexToNameMap[idx];
        // if it's not found it means the emcc linker didn't include it, which is fine
        if (shortName !== undefined) {
            const stubFn = env[shortName];
            if (typeof stubFn !== "function") throw new Error(`Expected ${shortName} to be a function`);
            env[shortName] = realFn;
        }
    }
}

View on GitHub (pinned to 60108ba66e)

Solutions

  1. Rebuild the .NET wasm runtime and its JS loader with the matching emscripten version so the import stubs are generated fresh.
  2. Remove any code that mutates the wasm imports object (env/a) before instantiation.
  3. If you fork the runtime, ensure replace_linker_placeholders runs before any external code touches imports.env / imports.a.

Example fix

// before: third-party code mutates imports
imports.env[shortName] = null;
// after: leave import stubs untouched until the runtime replaces them
// (do not overwrite imports.env[shortName])
Defensive patterns

Strategy: validation

Validate before calling

// before calling replace_linker_placeholders (or before instantiating the wasm)
const env = imports.env || imports.a;
if (!env) throw new Error('emscripten imports.env/.a missing');
for (const k of Object.keys(env)) {
  if (typeof env[k] !== 'function')
    console.warn(`import '${k}' is not a function before linker replacement`);
}

Type guard

function isFunctionImports(env: Record<string, unknown>): env is Record<string, Function> {
  return Object.values(env).every(v => typeof v === 'function');
}

Prevention

When it happens

Trigger: Instantiating a custom/rebuilt dotnet wasm module whose import object was mutated externally; shipping a runtime bundle produced by a mismatched emscripten toolchain; a library initializer or third-party code overwriting Module imports before replace_linker_placeholders runs.

Common situations: Forking the runtime and editing the wasm imports; upgrading emscripten without rebuilding the .NET wasm runtime; an aggressive monkey-patch on the emscripten Module that replaces import stubs with non-function values.

Related errors


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