dotnet/runtime · error · Error

JSObject proxy is not supported for ${jsType} ${value}

Error message

JSObject proxy is not supported for ${jsType} ${value}

What it means

The final fallback in marshalCsObjectToCs: the JS value has no GCHandle, typeof is not string/number/boolean/bigint/object-handled, and is not an array/date/error/thenable/span. The current case in practice is typeof === "function": a raw JS function passed where a marshaled object was expected.

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 290d5ab72c)

Solutions

  1. If you intend to pass a callable, declare the C# parameter as a delegate (Action/Func) with the appropriate [JSMarshalAsAttribute<JSType.Function>] so the function marshaler is used.
  2. Otherwise, serialize the value to a plain object or string before marshaling.

Example fix

// before
[JSImport("globalThis.fns.run")]
static partial void Run(object cb);
Run(callbackFn); // JS function -> error

// after
[JSImport("globalThis.fns.run")]
static partial void Run([JSMarshalAsAttribute<JSType.Function>] Action cb);
Defensive patterns

Strategy: type-guard

Validate before calling

function prepare(value: unknown): unknown {
    if (typeof value === "function") {
        throw new TypeError("Pass a delegate marshaler for callbacks, not object");
    }
    return value;
}

Type guard

const isPlainDataForCsObject = (v: unknown): boolean =>
    v === null || v === undefined ||
    ["string", "number", "boolean"].includes(typeof v) ||
    (typeof v === "object" && typeof (v as any) !== "function");

Prevention

When it happens

Trigger: Passing a JS function reference to a C# method whose parameter is typed `object` rather than as a delegate/function handle. Also passing exotic host objects whose typeof returns an unsupported category.

Common situations: Trying to send a callback function to C# via an object-typed parameter instead of using a delegate/Action marshaler; passing a Proxy or class constructor where a data object was expected.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/e7a7388fe6a53cf2. Report an issue: GitHub.