dotnet/runtime · error · Error
${function_name} must be a Function but was ${typeof fn}
Error message
${function_name} must be a Function but was ${typeof fn} What it means
Thrown by mono_wasm_lookup_js_import after successfully resolving the final property of a [JSImport] name, when that resolved value is not a function (typeof !== 'function'). It signals the attribute points to a non-callable export such as a constant, object, or undefined.
Source
Thrown at src/mono/browser/runtime/invoke-js.ts:413
} else if (parts[0] === "globalThis") {
scope = globalThis;
parts.shift();
}
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
const newscope = scope[part];
if (!newscope) {
throw new Error(`${part} not found while looking up ${function_name}`);
}
scope = newscope;
}
const fname = parts[parts.length - 1];
const fn = scope[fname];
if (typeof (fn) !== "function") {
throw new Error(`${function_name} must be a Function but was ${typeof fn}`);
}
// if the function was already bound to some object it would stay bound to original object. That's good.
return fn.bind(scope);
}
export function set_property (self: any, name: string, value: any): void {
mono_check(self, "Null reference");
self[name] = value;
}
export function get_property (self: any, name: string): any {
mono_check(self, "Null reference");
return self[name];
}
export function has_property (self: any, name: string): boolean {
mono_check(self, "Null reference");View on GitHub (pinned to 290d5ab72c)
Solutions
- Confirm the final segment of the JSImport path resolves to a function (typeof check in the JS module).
- Update the JSImport attribute to the correct function export name.
- If interop needs a non-function value, use a wrapper JS function that returns it instead of importing the value directly.
- Ensure the module is imported via JSHost.ImportAsync so the export is populated.
Example fix
// before: importing a non-function export
// JS: export const config = { url: '...' };
// C#: [JSImport("myMod.config")] static partial string GetUrl();
// after: expose a function
// JS: export function getUrl() { return config.url; }
// C#: [JSImport("myMod.getUrl")] static partial string GetUrl(); Defensive patterns
Strategy: type-guard
Validate before calling
const fn = scope[fname];
if (typeof fn !== 'function') {
// the export is missing or not callable; fix the JS export or the attribute
} Type guard
function isCallable(scope: any, name: string): boolean {
return typeof scope?.[name] === 'function';
} Prevention
- Export functions (not constants) for [JSImport] targets, or wrap values in accessor functions.
- Add a unit test asserting each [JSImport] target resolves to a function in the JS module.
- Re-check exports after refactoring JS modules.
When it happens
Trigger: Produced when the last segment of a [JSImport] name resolves to a non-function value. E.g. [JSImport("myMod.config")] where `config` is an object/string, or the export does not exist on the module (resolves to undefined).
Common situations: Attribute points to a property/constant instead of a function; the export was renamed; the default export is referenced as if it were a named function; the module exports an object whose member is not a function.
Related errors
- ${part} not found while looking up ${function_name}
- NotImplementedException: bigint
- NotImplementedException: TypedArray
- JSObject proxy is not supported for ${js_type} ${value}
- ${part} not found while looking up ${functionName}
AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06).
Data as JSON: /api/errors/8ada58bb0c30519a.
Report an issue: GitHub.