{"record":{"id":"b770fd11af5b83f3","repo":"dotnet/aspnetcore","slug":"could-not-find-identifier-key-was-undef","errorCode":null,"errorMessage":"Could not find '${identifier}' ('${key}' was undefined).","messagePattern":"Could not find '(.+?)' \\('(.+?)' was undefined\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts","lineNumber":604,"sourceCode":"   * Traverses the object hierarchy to find an object member specified by the identifier.\n   *\n   * @param obj Root object to search in.\n   * @param identifier Complete identifier of the member to find, e.g. \"document.location.href\".\n   * @returns A tuple containing the immediate parent of the member and the member name.\n   */\n  export function findObjectMember(obj: any, identifier: string): [any, string] {\n      const keys = identifier.split(\".\");\n      let current = obj;\n\n      // First, we iterate over all but the last key. We throw error for missing intermediate keys.\n      // Error handling in case of undefined last key depends on the type of operation.\n      for (let index = 0; index < keys.length - 1; index++) {\n          const key = keys[index];\n\n          if (current && typeof current === \"object\" && key in current) {\n              current = current[key];\n          } else {\n              throw new Error(`Could not find '${identifier}' ('${key}' was undefined).`);\n          }\n      }\n\n      return [\n          current,\n          keys[keys.length - 1]\n      ];\n  }\n\n  // Takes an object member and a call type and returns a function that performs the operation specified by the call type on the member.\n  //\n  // @param parent Immediate parent of the accessed object member.\n  // @param memberName Name (key) of the accessed member.\n  // @param callType The type of the operation to perform on the member.\n  // @param identifier The full member identifier. Only used for error messages.\n  // @returns A function that performs the operation on the member.\n  //\n  function wrapJSCallAsFunction(parent: any, memberName: string, callType: JSCallType, identifier: string): Function {","sourceCodeStart":586,"sourceCodeEnd":622,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L586-L622","documentation":"Thrown by DotNet.findObjectMember() while traversing dotted identifiers (e.g. 'document.location.href') when an intermediate segment is missing on the current object. The function walks all keys except the last; if any intermediate key is absent or current is not an object, the path is broken and the identifier cannot resolve.","triggerScenarios":".NET invokes a JS member by dotted identifier and one of the intermediate segments doesn't exist, e.g. calling 'myApp.helper.doThing' when window.myApp exists but myApp.helper is undefined. The error names the first missing intermediate key.","commonSituations":"JS library loaded conditionally or asynchronously and not present when interop runs; typo in the identifier on the .NET side; library API renamed between versions; script tag ordering issues.","solutions":["Verify the full dotted path exists in the browser console at call time: window.myApp.helper.doThing.","Ensure the JS library is loaded before the .NET interop call (load in wwwroot index.html, await Blazor start).","Correct typos or renamed APIs in the .NET Invoke identifier string."],"exampleFix":"// before (C#)\nawait JS.InvokeVoidAsync(\"myApp.helpers.doThing\");\n// JS only exposes window.myApp.doThing\n\n// after (C# matches the real path)\nawait JS.InvokeVoidAsync(\"myApp.doThing\");","handlingStrategy":"validation","validationCode":"// In JS, expose a helper that asserts the path exists before .NET calls it.\nfunction pathExists(root, dotted) { let c = root; for (const k of dotted.split('.')) { if (!c || !(k in c)) return false; c = c[k]; } return true; }","typeGuard":"function hasMember(root: any, dotted: string): boolean { let c = root; for (const k of dotted.split('.')) { if (c == null || !(k in c)) return false; c = c[k]; } return true; }","tryCatchPattern":"try { await JS.InvokeVoidAsync(\"myApp.helper.doThing\"); } catch (JSException e) when (e.Message.Contains(\"Could not find\")) { /* log identifier, ensure script loaded */ }","preventionTips":["Load JS libraries before Blazor makes interop calls (place scripts in wwwroot index.html).","Double-check dotted identifiers against the actual JS object graph in the console.","After library upgrades, re-verify identifier paths used in .NET Invoke strings."],"tags":["js-interop","blazor","identifier","object-graph","interop"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}