dotnet/aspnetcore · error

The value '${identifier}' is not a function.

Error message

The value '${identifier}' is not a function.

What it means

Thrown by wrapJSCallAsFunction for JSCallType.FunctionCall: the member resolved from the identifier exists but is not an instance of Function. The framework calls func.bind(parent) and invokes it; a non-callable value (number, object, getter that returns a non-function) cannot be invoked.

Source

Thrown at src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts:629

      ];
  }

  // Takes an object member and a call type and returns a function that performs the operation specified by the call type on the member.
  //
  // @param parent Immediate parent of the accessed object member.
  // @param memberName Name (key) of the accessed member.
  // @param callType The type of the operation to perform on the member.
  // @param identifier The full member identifier. Only used for error messages.
  // @returns A function that performs the operation on the member.
  //
  function wrapJSCallAsFunction(parent: any, memberName: string, callType: JSCallType, identifier: string): Function {
      switch (callType) {
      case JSCallType.FunctionCall:
          const func = parent[memberName];
          if (func instanceof Function) {
              return func.bind(parent);
          }
          throw new Error(`The value '${identifier}' is not a function.`);

      case JSCallType.ConstructorCall:
          const ctor = parent[memberName];
          if (ctor instanceof Function) {
              const bound = ctor.bind(parent);
              return (...args: any[]) => new bound(...args);
          }
          throw new Error(`The value '${identifier}' is not a function.`);

      case JSCallType.GetValue:
          if (!isReadableProperty(parent, memberName)) {
              throw new Error(`The property '${identifier}' is not defined or is not readable.`);
          }
          return () => parent[memberName];
      case JSCallType.SetValue:
          if (!isWritableProperty(parent, memberName)) {
              throw new Error(`The property '${identifier}' is not writable.`);
          }

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Check the JS exports: ensure the identifier points to a function (typeof x === 'function').
  2. Rename so the function and the data property do not collide.
  3. If you meant to read a value, use the read/get path instead of the call path on the .NET side.
  4. Add a thin wrapper function in JS that returns the value.

Example fix

// before
// lib.js
export const count = 0; // data, not fn
// C#: await JS.InvokeAsync<int>("lib.count");

// after
// lib.js
export function getCount() { return 0; }
// C#: await JS.InvokeAsync<int>("lib.getCount");
Defensive patterns

Strategy: type-guard

Validate before calling

// shim: assert callable before exposing
export function ensureFn(obj, id) {
  const fn = id.split('.').reduce((o,k)=>o?.[k], obj);
  if (typeof fn !== 'function') throw new TypeError(`${id} is not a function`);
  return fn;
}

Type guard

function isCallable(v: any): v is Function { return typeof v === 'function'; }

Try / catch

try { await JS.InvokeAsync('lib.fn'); } catch (e) { if (/is not a function/i.test(e.message)) { /* fix identifier */ } else throw e; }

Prevention

When it happens

Trigger: Calling .invokeMethod('myLib.value') where 'value' is a property holding data, not a function; invoking a getter; calling a key that holds an object/array. Distinguish from error 84 (missing) — here the member exists but is not callable.

Common situations: Naming collision between a data property and the function you meant to call; refactor that turned a function into a property; calling a namespace object as if it were a function.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/47ba4c4bce82054f. Report an issue: GitHub.