{"record":{"id":"7ffd2e2b32d3a7b1","repo":"dotnet/runtime","slug":"part-not-found-while-looking-up-functionname","errorCode":null,"errorMessage":"${part} not found while looking up ${functionName}","messagePattern":"(.+?) not found while looking up (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/invoke-js.ts","lineNumber":286,"sourceCode":"\n    let scope: any = {};\n    const parts = functionName.split(\".\");\n    if (jsModuleName) {\n        scope = importedModules.get(jsModuleName);\n        dotnetAssert.fastCheck(scope, () => `ES6 module ${jsModuleName} was not imported yet, please call JSHost.ImportAsync() first in order to invoke ${functionName}.`);\n    } else if (parts[0] === \"INTERNAL\") {\n        scope = dotnetApi.INTERNAL;\n        parts.shift();\n    } else if (parts[0] === \"globalThis\") {\n        scope = globalThis;\n        parts.shift();\n    }\n\n    for (let i = 0; i < parts.length - 1; i++) {\n        const part = parts[i];\n        const newscope = scope[part];\n        if (!newscope) {\n            throw new Error(`${part} not found while looking up ${functionName}`);\n        }\n        scope = newscope;\n    }\n\n    const fname = parts[parts.length - 1];\n    const fn = scope[fname];\n\n    if (typeof (fn) !== \"function\") {\n        throw new Error(`${functionName} must be a Function but was ${typeof fn}`);\n    }\n\n    // if the function was already bound to some object it would stay bound to original object. That's good.\n    return fn.bind(scope);\n}\n\nexport function invokeJSFunction(functionJSHandle: JSHandle, args: JSMarshalerArguments): void {\n    assertRuntimeRunning();\n    const boundFn = getJSObjectFromJSHandle(functionJSHandle);","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/invoke-js.ts#L268-L304","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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.')."],"exampleFix":"// before - C#\n[JSImport(\"globals.math.add\", \"m\")]\n// m.js exports: export const math = { add: ... } but 'globals' does not exist\n\n// after\n[JSImport(\"math.add\", \"m\")]\n// or fix the module so the dotted path resolves","handlingStrategy":"type-guard","validationCode":"// Before binding, confirm every intermediate segment of the dotted path exists.\nfunction resolveDottedPath(scope: any, dotted: string): any | undefined {\n  let s = scope;\n  for (const part of dotted.split(\".\").slice(0, -1)) {\n    if (s == null || !(part in s)) return undefined;\n    s = s[part];\n  }\n  return s;\n}\n\nconst mod = await JSHost.ImportAsync(\"m\", \"./m.js\");\nconst inter = resolveDottedPath(mod, \"a.b.fn\");\nif (inter === undefined) console.error(\"intermediate missing for a.b.fn\");","typeGuard":"function hasNestedProperty(obj: any, path: string): boolean {\n  let s = obj;\n  for (const p of path.split(\".\")) {\n    if (s == null || typeof s !== \"object\" || !(p in s)) return false;\n    s = s[p];\n  }\n  return true;\n}","tryCatchPattern":"null","preventionTips":["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."],"tags":["jsinterop","jsimport","binding","module-lookup"],"backgroundTag":null,"analyzedSha":"60108ba66eb7d1d12f595480091b4ad80a24b172","analyzedAt":"2026-08-10T18:54:11.478Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}