{"record":{"id":"47ba4c4bce82054f","repo":"dotnet/aspnetcore","slug":"the-value-identifier-is-not-a-function","errorCode":null,"errorMessage":"The value '${identifier}' is not a function.","messagePattern":"The value '(.+?)' is not a function\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts","lineNumber":629,"sourceCode":"      ];\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 {\n      switch (callType) {\n      case JSCallType.FunctionCall:\n          const func = parent[memberName];\n          if (func instanceof Function) {\n              return func.bind(parent);\n          }\n          throw new Error(`The value '${identifier}' is not a function.`);\n\n      case JSCallType.ConstructorCall:\n          const ctor = parent[memberName];\n          if (ctor instanceof Function) {\n              const bound = ctor.bind(parent);\n              return (...args: any[]) => new bound(...args);\n          }\n          throw new Error(`The value '${identifier}' is not a function.`);\n\n      case JSCallType.GetValue:\n          if (!isReadableProperty(parent, memberName)) {\n              throw new Error(`The property '${identifier}' is not defined or is not readable.`);\n          }\n          return () => parent[memberName];\n      case JSCallType.SetValue:\n          if (!isWritableProperty(parent, memberName)) {\n              throw new Error(`The property '${identifier}' is not writable.`);\n          }","sourceCodeStart":611,"sourceCodeEnd":647,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L611-L647","documentation":"Thrown by wrapJSCallAsFunction() in the FunctionCall case when the resolved member on the parent is not an instance of Function. .NET requested a function invocation (JSCallType.FunctionCall) but the named property is a value, getter returning non-function, or missing-as-value. The identifier in the message is the full dotted name.","triggerScenarios":".NET calls JS.InvokeAsync('myObj.myValue') expecting a function, but myValue is a number/string/object. Distinct from a missing member (that throws in findObjectMember for intermediates, or isReadableProperty for GetValue); here the member exists but is not callable.","commonSituations":"Calling a property that holds data instead of a method (e.g. myObj.count vs myObj.getCount()); API renamed from method to property across versions; calling a constructor as a function or vice versa.","solutions":["Check whether the identifier is actually a function in the browser: typeof window.myObj.myValue === 'function'.","Update the .NET Invoke identifier to the correct method name.","If you meant to read a value, use JS.InvokeAsync<T> which uses GetValue semantics, not a function call."],"exampleFix":"// before (C#)\nawait JS.InvokeVoidAsync(\"myObj.count\"); // count is a number\n\n// after\nvar count = await JS.InvokeAsync<int>(\"myObj.count\"); // value read\n// or call the real method\nawait JS.InvokeVoidAsync(\"myObj.increment\");","handlingStrategy":"type-guard","validationCode":"function isCallableMember(root, dotted) { const parts = dotted.split('.'); let c = root; for (let i = 0; i < parts.length - 1; i++) c = c?.[parts[i]]; return typeof c?.[parts[parts.length - 1]] === 'function'; }","typeGuard":"function isFunctionMember(root: any, dotted: string): boolean { const parts = dotted.split('.'); let c = root; for (let i = 0; i < parts.length - 1; i++) c = c?.[parts[i]]; return typeof c?.[parts.at(-1)] === 'function'; }","tryCatchPattern":"try { await JS.InvokeVoidAsync(\"myObj.doThing\"); } catch (JSException e) when (e.Message.Contains(\"is not a function\")) { /* verify typeof in console, fix identifier */ }","preventionTips":["Confirm typeof member === 'function' in the browser before wiring the .NET Invoke.","Use value-read interop (InvokeAsync<T>) for properties, function-call interop for methods.","Keep method names consistent across JS refactors and .NET call sites."],"tags":["js-interop","blazor","identifier","invocation","type-mismatch"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}