dotnet/runtime · error · Error

${part} not found while looking up ${functionName}

Error message

${part} not found while looking up ${functionName}

What it means

lookupJsImport walks the dotted function name (e.g. 'MyModule.Sub.Fn') through the imported JS module's scope; if any intermediate segment is undefined it throws, naming the missing part. This is the JSImport resolution path used by [JSImport] bindings after JSHost.ImportAsync has loaded the module.

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 290d5ab72c)

Solutions

  1. Call and await JSHost.ImportAsync(moduleName, url) before invoking any JSImport from that module.
  2. Verify the dotted name in [JSImport] matches the actual nested export path — log the imported module object to inspect its shape.
  3. Check for typos and casing in the function name and every intermediate segment.

Example fix

// before
[JSImport("Sub.Fn", "mod")] // 'Sub' missing on module -> "Sub not found while looking up Sub.Fn"

// after: import first, and match the export structure
await JSHost.ImportAsync("mod", "./mod.js");
// mod.js: export const Sub = { Fn: () => {} };
Defensive patterns

Strategy: validation

Validate before calling

function resolvePath(module, dotted) {
  let scope = module;
  const parts = dotted.split(".");
  for (const part of parts.slice(0, -1)) {
    if (scope == null || !(part in scope)) return false;
    scope = scope[part];
  }
  return typeof scope[parts[parts.length - 1]] === "function";
}
if (!resolvePath(importedModule, "Sub.Fn")) { /* wrong path / not imported */ }

Try / catch

// C# caller: lookupJsImport errors are marshaled to .NET as JSException
try { await jsImport.Fn(); }
catch (JSException ex) when (ex.Message.Contains("not found while looking up")) {
  // module path wrong, not imported, or typo — fix the [JSImport] name / call ImportAsync
}

Prevention

When it happens

Trigger: A [JSImport('A.B.C', 'mod')] whose module object lacks the expected nested path — the module wasn't imported yet, the dotted name has a typo or wrong casing, or the JS export isn't nested where the binding expects.

Common situations: Forgetting to await JSHost.ImportAsync('mod', url) before invoking a JSImport; renaming/moving a JS export without updating the attribute; a mismatch between the module's real export shape and the dotted name; casing differences on case-sensitive hosts.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/7ffd2e2b32d3a7b1. Report an issue: GitHub.