dotnet/runtime · error · Error

address must be a location in the native heap

Error message

address must be a location in the native heap

What it means

Thrown by mono_wasm_new_external_root when the supplied address is falsy (0, null, undefined, or NaN-coerced-to-0). An external root must point at an existing location in the native heap; a null address has no slot for the runtime to register, so creation is rejected.

Source

Thrown at src/mono/browser/runtime/roots.ts:52

    const offset = malloc(capacityBytes);
    if ((<any>offset % 4) !== 0)
        throw new Error("Malloc returned an unaligned offset");

    _zero_region(offset, capacityBytes);

    return new WasmRootBufferImpl(offset, capacity, true, name);
}

/**
 * Allocates a WasmRoot pointing to a root provided and controlled by external code. Typicaly on managed stack.
 * Releasing this root will not de-allocate the root space. You still need to call .release().
 */
export function mono_wasm_new_external_root<T extends MonoObject> (address: VoidPtr | MonoObjectRef): WasmRoot<T> {
    if (WasmEnableThreads && runtimeHelpers.disableManagedTransition) throw new Error("External roots are not supported in multithreaded mode");
    let result: WasmExternalRoot<T>;

    if (!address)
        throw new Error("address must be a location in the native heap");

    if (_external_root_free_instances.length > 0) {
        result = _external_root_free_instances.pop()!;
        result._set_address(address);
    } else {
        result = new WasmExternalRoot<T>(address);
    }

    return result;
}

/**
 * Allocates temporary storage for a pointer into the managed heap.
 * Pointers stored here will be visible to the GC, ensuring that the object they point to aren't moved or collected.
 * If you already have a managed pointer you can pass it as an argument to initialize the temporary storage.
 * The result object has get() and set(value) methods, along with a .value property.
 * When you are done using the root you must call its .release() method.
 */

View on GitHub (pinned to 60108ba66e)

Solutions

  1. Ensure the address argument is a real native-heap location (e.g. from malloc or a stack alloc) before calling the API.
  2. Guard: skip external-root creation when the address is 0/null/undefined.
  3. Debug-print the address source upstream to find where the zero value originates.

Example fix

// before
const root = mono_wasm_new_external_root(maybeNullPtr);
// after
if (!maybeNullPtr) throw new Error('cannot create external root: slot not allocated');
const root = mono_wasm_new_external_root(maybeNullPtr);
Defensive patterns

Strategy: type-guard

Validate before calling

function newExternalRootSafe(addr: VoidPtr | MonoObjectRef | null) {
  if (!addr) throw new Error('slot not allocated');
  return mono_wasm_new_external_root(addr);
}

Type guard

function isNonNullAddress(a: unknown): a is number {
  return typeof a === 'number' && a > 0;
}

Prevention

When it happens

Trigger: Calling mono_wasm_new_external_root(0), mono_wasm_new_external_root(null), mono_wasm_new_external_root(undefined), or passing a pointer variable that was never assigned a real native-heap address.

Common situations: Using an uninitialized pointer field as the root address; passing a MonoObjectRef that was zeroed; logic that conditionally allocates a native slot but always calls the root API; off-by-one that yields address 0.

Related errors


AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10). Data as JSON: /api/errors/3aef11c901306776. Report an issue: GitHub.