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
- Wrap the primitive in an object before returning: return { value: myString };
- 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
- Ensure JS interop functions that map to IJSObjectReference return an object, not a primitive.
- Keep .NET interop return types and JS return values in sync across refactors.
- Wrap primitives in an object if a reference handle is truly required.
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
- Cannot create a JSStreamReference from the value
- JS object instance with Id
- Byte array index ' ' does not exist.
- Cannot create a JSStreamReference from the value
- Invalid JS call result type
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)