{"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":null,"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/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L586-L622","documentation":"Thrown by findObjectMember while walking the dotted identifier path. For an identifier like 'document.location.href' it walks each key except the last; if any intermediate key is absent (the property is not 'in' the current object, or current is null) it throws, naming the full identifier and the first missing segment.","triggerScenarios":"JS interop with a dotted identifier where an intermediate property is missing, e.g. JS.InvokeAsync('myLib.notThere.thing') or invoking 'window.someApi.method' when someApi is undefined. Also triggers when the root object itself is null (e.g. a JS object reference whose underlying global was removed).","commonSituations":"Referencing a browser API that isn't loaded yet (script ordering); typo in an identifier string; library not loaded at invocation time; SSR/prerender where window/document are unavailable; version change removing/renaming a property.","solutions":["Verify the identifier string exactly matches the loaded JS API (check spelling, casing, dots).","Ensure the script providing myLib has loaded before the interop call; run interop in OnAfterRenderAsync, not during prerender.","For environment-optional APIs, expose a wrapper JS function that returns a safe default rather than calling the dotted path directly.","Use a try/catch around the interop and fall back gracefully."],"exampleFix":"// before\nawait JS.InvokeVoidAsync(\"window.experimentalApi.configure\", opts);\n\n// after\nawait JS.InvokeVoidAsync(\"myShim.configureExperimental\", opts);\n// myShim.js\nexport function configureExperimental(opts) {\n  if (window.experimentalApi?.configure) window.experimentalApi.configure(opts);\n}","handlingStrategy":"validation","validationCode":"// shim that checks the path exists before interop\nexport function resolve(path) {\n  return path.split('.').reduce((o,k) => (o == null ? o : o[k]), window);\n}","typeGuard":"function pathExists(root:any, id:string):boolean {\n  return id.split('.').every((k,i,a) => { root = root?.[k]; return root != null || i === a.length-1; });\n}","tryCatchPattern":"try { await JS.InvokeVoidAsync('lib.deep.fn'); }\ncatch (e) { if (/Could not find/.test(e.message)) { /* load lib or fallback */ } else throw e; }","preventionTips":["Run interop in OnAfterRenderAsync, never during prerender.","Verify the script providing the global has loaded.","Double-check identifier spelling and casing."],"tags":["blazor","jsinterop","identifiers","browser-api","prerender"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}