dotnet/runtime · error · Error

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

Error message

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

What it means

The final else in marshal_cs_object_to_cs fires when the JS value's typeof is not string, number, bigint, boolean, or object. In practice this means a Symbol or a Function passed where the C# parameter is object/JSObject, neither of which has an object-marshaler mapping.

Source

Thrown at src/mono/browser/runtime/marshal-to-cs.ts:430

                || value instanceof Int8Array
                || value instanceof Uint8ClampedArray
                || value instanceof Uint16Array
                || value instanceof Uint32Array
            ) {
                throw new Error("NotImplementedException: TypedArray");
            } else if (isThenable(value)) {
                marshal_task_to_cs(arg, value);
            } else if (value instanceof Span) {
                throw new Error("NotImplementedException: Span");
            } else if (js_type == "object") {
                const js_handle = mono_wasm_get_js_handle(value);
                set_arg_type(arg, MarshalerType.JSObject);
                if (BuildConfiguration === "Debug" && Object.isExtensible(value)) {
                    value[proxy_debug_symbol] = `JS Object with JSHandle ${js_handle}`;
                }
                set_js_handle(arg, js_handle);
            } else {
                throw new Error(`JSObject proxy is not supported for ${js_type} ${value}`);
            }
        } else {
            assert_not_disposed(value);
            if (value instanceof ArraySegment) {
                throw new Error("NotImplementedException: ArraySegment. " + jsinteropDoc);
            } else if (value instanceof ManagedError) {
                set_arg_type(arg, MarshalerType.Exception);
                set_gc_handle(arg, gc_handle);
            } else if (value instanceof ManagedObject) {
                set_arg_type(arg, MarshalerType.Object);
                set_gc_handle(arg, gc_handle);
            } else {
                throw new Error("NotImplementedException " + js_type + ". " + jsinteropDoc);
            }
        }
    }
}

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. For functions, pass them through a parameter typed to accept a JS function proxy, or wrap them in an object whose handle can be obtained.
  2. Convert Symbols to strings; convert functions to a serializable representation or invoke them in JS and pass the result.
  3. Type the C# parameter to match the actual JS value kind rather than object.

Example fix

// before
dotnet.jsExports.Take(Symbol('id')); // throws
// after
dotnet.jsExports.Take('id'); // pass a string
Defensive patterns

Strategy: type-guard

Validate before calling

const t = typeof value;
if (t === 'symbol' || t === 'function') {
  throw new TypeError(`${t} is not supported by the object marshaler; convert to a supported type.`);
}

Type guard

function isUnsupportedObjectKind(v: unknown): boolean { const t = typeof v; return t === 'symbol' || t === 'function'; }

Prevention

When it happens

Trigger: Passing a JS Symbol or a Function to a C# [JSExport] parameter typed as object/JSObject.

Common situations: Sending a callback function or a Symbol to a generic object parameter expecting a data object; forgetting to wrap a function in a JSObject-backed holder.

Related errors


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