dotnet/aspnetcore · error · Error

Cannot create a JSObjectReference from the value

Error message

Cannot create a JSObjectReference from the value '${jsObject}'.

What it means

Thrown by DotNet.createJSObjectReference() when the supplied value is not null, not undefined, and not a JS object or Function. The interop layer can only track references to real objects/functions; primitives cannot be cached by ID. Null/undefined short-circuit to a sentinel ID of -1, so this error only fires for non-object truthy values.

Solutions

  1. Wrap the primitive in an object before returning: return { value: myString };
  2. Change the .NET interop return type from IJSObjectReference to the matching primitive (string, int, etc.) so createJSCallResult uses JSCallResultType.Default.

Example fix

// before (JS interop function returning a primitive)
DotNet.invokeMethodAsync('MyLib', 'GetToken').then(ref => {
    // .NET declared IJSObjectReference -> createJSObjectReference('abc') throws
});

// after
// .NET side: change Task<string> instead of Task<IJSObjectReference>
// or JS side: return an object wrapper
return { token: myToken };
Defensive patterns

Strategy: type-guard

Validate before calling

function isObjectLike(v) { return v !== null && v !== undefined && (typeof v === 'object' || typeof v === 'function'); }
if (!isObjectLike(value)) { throw new TypeError('Expected an object or function for JSObjectReference'); }
const ref = DotNet.createJSObjectReference(value);

Type guard

function isJSObjectReferenceable(v: unknown): v is object | Function { return v !== null && v !== undefined && (typeof v === 'object' || typeof v === 'function'); }

Try / catch

try { return DotNet.createJSObjectReference(value); } catch (e) { throw new Error(`Interop return value must be an object; got ${typeof value}.`, { cause: e }); }

Prevention

When it happens

Trigger: Calling createJSObjectReference with a primitive: a string, number, boolean, or bigint. Also fires when a JS interop call is declared to return JSObjectReference in .NET ([JSInvokable] returning a string), because the runtime calls createJSCallResult -> createJSObjectReference on the return value.

Common situations: Returning a primitive from a JS interop function whose .NET caller expects IJSObjectReference; passing a plain string where DotNet expects an object reference; upgrading an interop method's return type from object to primitive without updating the .NET side.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/dad4d19e25ef68a0. Report an issue: GitHub.

Appendix: source

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

      if (jsObject === null || jsObject === undefined) {
          return {
              [jsObjectIdKey]: -1
          };
      }

      if (jsObject && (typeof jsObject === "object" || jsObject instanceof Function)) {
          cachedJSObjectsById[nextJsObjectId] = new JSObject(jsObject);

          const result = {
              [jsObjectIdKey]: nextJsObjectId
          };

          nextJsObjectId++;

          return result;
      }

      throw new Error(`Cannot create a JSObjectReference from the value '${jsObject}'.`);
  }

  /**
   * Creates a JavaScript data reference that can be passed to .NET via interop calls.
   *
   * @param streamReference The ArrayBufferView or Blob used to create the JavaScript stream reference.
   * @returns The JavaScript data reference (this will be the same instance as the given object).
   * @throws Error if the given value is not an Object or doesn't have a valid byteLength.
   */
  export function createJSStreamReference(streamReference: ArrayBuffer | ArrayBufferView | Blob | any): any {
      let length = -1;

      // If we're given a raw Array Buffer, we interpret it as a `Uint8Array` as
      // ArrayBuffers' aren't directly readable.
      if (streamReference instanceof ArrayBuffer) {
          streamReference = new Uint8Array(streamReference);
      }

View on GitHub (pinned to 3600ca084e)