dotnet/runtime · error · Error
not found while looking up
Error message
${part} not found while looking up ${functionName} What it means
lookupJsImport() resolves a dotted JSImport function name (e.g. 'MyModule.subNamespace.helper') against an imported ES module, dotnetApi.INTERNAL, or globalThis. It walks intermediate parts; if any intermediate property is missing (newscope is falsy) it throws naming the missing part and the full function name being looked up.
Solutions
- Verify the exact dotted path in the [JSImport(...)] attribute matches the shape of the object exported by the JS module registered via JSHost.ImportAsync.
- Log/inspect the imported module object right after JSHost.ImportAsync to confirm each intermediate segment exists.
- Correct typos in intermediate namespace segments or update the JS module to export the expected nested structure.
- If using globalThis/INTERNAL prefixes, confirm the prefix is spelled exactly ('globalThis.', 'INTERNAL.').
Example fix
// before - C#
[JSImport("globals.math.add", "m")]
// m.js exports: export const math = { add: ... } but 'globals' does not exist
// after
[JSImport("math.add", "m")]
// or fix the module so the dotted path resolves Defensive patterns
Strategy: type-guard
Validate before calling
// Before binding, confirm every intermediate segment of the dotted path exists.
function resolveDottedPath(scope: any, dotted: string): any | undefined {
let s = scope;
for (const part of dotted.split(".").slice(0, -1)) {
if (s == null || !(part in s)) return undefined;
s = s[part];
}
return s;
}
const mod = await JSHost.ImportAsync("m", "./m.js");
const inter = resolveDottedPath(mod, "a.b.fn");
if (inter === undefined) console.error("intermediate missing for a.b.fn"); Type guard
function hasNestedProperty(obj: any, path: string): boolean {
let s = obj;
for (const p of path.split(".")) {
if (s == null || typeof s !== "object" || !(p in s)) return false;
s = s[p];
}
return true;
} Try / catch
null
Prevention
- Keep the [JSImport] dotted name in sync with the JS module's export shape; update both on refactor.
- Log/inspect the imported module object after JSHost.ImportAsync to verify each segment exists.
- Add a unit test that resolves each imported function name before C# binding runs.
- Watch for typos and for renamed/removed intermediate namespaces.
When it happens
Trigger: A [JSImport] attribute names a function path where an intermediate segment is absent on the imported module object. E.g. JSHost.ImportAsync('m','./m.js') registered a module whose export is { fn: ... } but the [DllImport] uses 'm.utils.fn' where 'utils' does not exist.
Common situations: Typo in the [JSImport] function name or module name; the JS module was refactored/renamed and an intermediate namespace was removed; the module export shape changed (named export moved); looking up a property on a function-valued export instead of an object.
Related errors
- must be a Function but was
- must be a Function but was
- not found while looking up
- JSObject proxy is not supported for
- not implemented
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/7ffd2e2b32d3a7b1.
Report an issue: GitHub.
Appendix: source
Thrown at src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/invoke-js.ts:286
let scope: any = {};
const parts = functionName.split(".");
if (jsModuleName) {
scope = importedModules.get(jsModuleName);
dotnetAssert.fastCheck(scope, () => `ES6 module ${jsModuleName} was not imported yet, please call JSHost.ImportAsync() first in order to invoke ${functionName}.`);
} else if (parts[0] === "INTERNAL") {
scope = dotnetApi.INTERNAL;
parts.shift();
} 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 ${functionName}`);
}
scope = newscope;
}
const fname = parts[parts.length - 1];
const fn = scope[fname];
if (typeof (fn) !== "function") {
throw new Error(`${functionName} 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 invokeJSFunction(functionJSHandle: JSHandle, args: JSMarshalerArguments): void {
assertRuntimeRunning();
const boundFn = getJSObjectFromJSHandle(functionJSHandle);View on GitHub (pinned to 60108ba66e)