dotnet/runtime · error · Error
Implicit conversion of roots to pointers is no longer suppor
Error message
Implicit conversion of roots to pointers is no longer supported. Use .value or .address as appropriate
What it means
Thrown by WasmJsOwnedRoot.valueOf() to prevent implicit coercion of a scratch root into a primitive. Older code relied on valueOf to treat a root as a raw pointer; this was removed because it silently hid the distinction between a managed pointer (.value) and the address slot (.address). Any operation that triggers JS valueOf (arithmetic, string template, ==) on a scratch root now fails loudly.
Source
Thrown at src/mono/browser/runtime/roots.ts:315
const destinationAddress = this.address;
cwraps.mono_wasm_copy_managed_pointer(destinationAddress, source);
}
copy_to_address (destination: MonoObjectRef): void {
const sourceAddress = this.address;
cwraps.mono_wasm_copy_managed_pointer(destination, sourceAddress);
}
get value (): T {
return this.get();
}
set value (value: T) {
this.set(value);
}
valueOf (): T {
throw new Error("Implicit conversion of roots to pointers is no longer supported. Use .value or .address as appropriate");
}
clear (): void {
// .set performs an expensive write barrier, and that is not necessary in most cases
// for clear since clearing a root cannot cause new objects to survive a GC
const address32 = this.__buffer.get_address_32(this.__index);
localHeapViewU32()[address32] = 0;
}
release (): void {
if (!this.__buffer)
throw new Error("No buffer");
const maxPooledInstances = 128;
if (_scratch_root_free_instances.length > maxPooledInstances) {
_mono_wasm_release_scratch_index(this.__index);
(<any>this).__buffer = null;
this.__index = 0;View on GitHub (pinned to 60108ba66e)
Solutions
- Replace implicit usage with root.value when you need the managed pointer, or root.address when you need the slot address.
- For null-pointer checks use (root.value === 0) or root.get() === 0 instead of root == 0.
- In template strings, interpolate root.value explicitly: `${root.value}`.
- Run a grep for arithmetic and template usage of root variables to catch every site.
Example fix
// before
if (root == 0) { ... }
const s = `ptr ${root}`;
// after
if (root.value === 0) { ... }
const s = `ptr ${root.value}`; Defensive patterns
Strategy: validation
Validate before calling
// Lint-time guard: never use a root where a number/string is expected. const v: number = root.value; // managed pointer const a: MonoObjectRef = root.address; // slot address
Type guard
function isWasmRoot<T>(r: unknown): r is WasmRoot<T> {
return !!r && typeof r === 'object' && 'value' in r && 'address' in r;
} Prevention
- Add an ESLint rule (no-restricted-syntax) forbidding root identifiers in BinaryExpression/TemplateLiteral.
- Code-review logging statements that interpolate roots.
- Replace == 0 null checks with explicit .value === 0.
When it happens
Trigger: Writing root + 1, `${root}`, root == 0, or any expression that forces JS to call valueOf() on a WasmJsOwnedRoot instance. Also triggered by passing the root where a number is expected by an untyped API.
Common situations: Legacy interop code from before the valueOf removal; migrating code that treated roots as pointers; debugging statements like console.log(`ptr=${root}`); using == against 0 to test for null pointers.
Related errors
- capacity >= 1
- value must be an address in the managed heap
- count_or_values must be either an array or a number greater
- Unrecognized asset behavior:${asset.behavior}, for asset ${a
- event must be an object, but got ${JSON.stringify(event)}
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/752af34e320e5854.
Report an issue: GitHub.