dotnet/runtime · error · Error
JSObject proxy is not supported for
Error message
JSObject proxy is not supported for ${jsType} ${value} What it means
In marshalCsObjectToCs, after handling primitives, Date, Error, known typed arrays, arrays, thenables, Span, and plain objects, the final else branch throws for any remaining JS type. In practice this is reached for values whose typeof is 'function' (a raw function passed where object marshaling is expected) because functions are not thenable and not otherwise matched in the gcHandle===undefined branch.
Solutions
- Type the C# parameter as the appropriate delegate (Action<...> / Func<...>) so it is routed through _marshalFunctionToCs instead of the object marshaler.
- If you genuinely need to pass a callable to C#, wrap it so it is marshaled as a JSObject or a typed delegate, not as a bare object.
- Avoid returning bare functions from interop methods typed as object; use a concrete delegate return type.
Example fix
// before - C#
[JSImport("setCb", "m")]
static partial void SetCallback([MarshalAs(typeof(object))] object cb);
// after
[JSImport("setCb", "m")]
static partial void SetCallback(Action<int> cb); Defensive patterns
Strategy: type-guard
Validate before calling
// Avoid passing a raw function through an object-typed boundary.
function wrapCallback(fn: (...args: any[]) => any): object {
// return an object holder so it marshals as JSObject, or type the C# side as a delegate
return { invoke: fn } as object;
} Type guard
function isPlainFunction(v: unknown): boolean {
return typeof v === "function";
} Try / catch
null
Prevention
- Type C# callback params as Action<...>/Func<...> delegates, not object.
- Wrap bare functions in an object holder if you must marshal them as JSObject.
- Never return bare functions from interop methods typed as object.
- Audit interop signatures so callbacks use the delegate marshaler.
When it happens
Trigger: Passing a JS function (callback) to a JSImport parameter that is typed as object/JSObject rather than as a delegate (Action/Func) marshaler; or returning a function from a JSImport whose return is typed object.
Common situations: Passing a callback where the C# signature expects object; forgetting to type a callback parameter as the right delegate marshaler so it falls into the object path.
Related errors
- JSObject proxy is not supported for
- not implemented
- NotImplementedException: ArraySegment.
- NotImplementedException: bigint
- NotImplementedException
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/e7a7388fe6a53cf2.
Report an issue: GitHub.
Appendix: source
Thrown at src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/marshal-to-cs.ts:401
|| value instanceof Int8Array
|| value instanceof Uint8ClampedArray
|| value instanceof Uint16Array
|| value instanceof Uint32Array
) {
throw new Error("NotImplementedException: TypedArray");
} else if (isThenable(value)) {
marshalTaskToCs(arg, value);
} else if (value instanceof Span) {
throw new Error("NotImplementedException: Span");
} else if (jsType == "object") {
const jsHandle = getJsHandleFromJSObject(value);
setArgType(arg, MarshalerType.JSObject);
if (BuildConfiguration === "Debug" && Object.isExtensible(value)) {
value[proxyDebugSymbol] = `JS Object with JSHandle ${jsHandle}`;
}
setJsHandle(arg, jsHandle);
} else {
throw new Error(`JSObject proxy is not supported for ${jsType} ${value}`);
}
} else {
assertNotDisposed(value);
if (value instanceof ArraySegment) {
throw new Error("NotImplementedException: ArraySegment. " + jsinteropDoc);
} else if (value instanceof ManagedError) {
setArgType(arg, MarshalerType.Exception);
setGcHandle(arg, gcHandle);
} else if (value instanceof ManagedObject) {
setArgType(arg, MarshalerType.Object);
setGcHandle(arg, gcHandle);
} else {
throw new Error("NotImplementedException " + jsType + ". " + jsinteropDoc);
}
}
}
}
View on GitHub (pinned to 60108ba66e)