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
- Rebuild the .NET wasm runtime and its JS loader with the matching emscripten version so the import stubs are generated fresh.
- Remove any code that mutates the wasm imports object (env/a) before instantiation.
- 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
- Do not mutate the emscripten Module imports object after the runtime is constructed.
- Rebuild the wasm runtime and JS loader together with the same emscripten version.
- Run any library initializer that patches imports before the runtime's replace_linker_placeholders step.
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
- cwrap ${name} not found or not a function
- Module.ready couldn't be redefined.
- .NET runtime has failed to start, because too much memory wa
- Failed on mono_wasm_get_dbg_command_info
- No active JS diagnostic session
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/1b69717a97bf02b4.
Report an issue: GitHub.