dotnet/runtime · error · Error

value was not an integer

Error message

value was not an integer

What it means

autoThrowI52 throws 'value was not an integer' when the native mono_wasm_f64_to_i52/u52 routine returns I52_ERROR_NON_INTEGRAL, meaning floor(value) != value. It is reached via setI52/setU52 (writing) and getI52/getU52 indirectly. These i52/u52 helpers store/load 52-bit integers in the wasm heap.

Source

Thrown at src/mono/browser/runtime/memory.ts:152

export function setI32_unchecked (offset: MemOffset, value: number): void {
    receiveWorkerHeapViews();
    Module.HEAP32[<any>offset >>> 2] = value;
}

export function setI32 (offset: MemOffset, value: number): void {
    assert_int_in_range(<any>value, -0x8000_0000, 0x7FFF_FFFF);
    receiveWorkerHeapViews();
    Module.HEAP32[<any>offset >>> 2] = value;
}

function autoThrowI52 (error: I52Error) {
    if (error === I52Error.NONE)
        return;

    switch (error) {
        case I52Error.NON_INTEGRAL:
            throw new Error("value was not an integer");
        case I52Error.OUT_OF_RANGE:
            throw new Error("value out of range");
        default:
            throw new Error("unknown internal error");
    }
}

/**
 * Throws for values which are not 52 bit integer. See Number.isSafeInteger()
 */
export function setI52 (offset: MemOffset, value: number): void {
    mono_check(Number.isSafeInteger(value), () => `Value is not a safe integer: ${value} (${typeof (value)})`);
    receiveWorkerHeapViews();
    const error = cwraps.mono_wasm_f64_to_i52(<any>offset, value);
    autoThrowI52(error);
}

/**

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Round the value before passing: Math.trunc(value) or Math.round(value).
  2. Validate with Number.isInteger(value) before calling the i52/u52 API.
  3. If you genuinely need fractions, store them as f64 via setF64 instead.

Example fix

// before
Module.setHeapI52(offset, value /* 3.5 */);
// after
Module.setHeapI52(offset, Math.trunc(value));
Defensive patterns

Strategy: validation

Validate before calling

if (!Number.isInteger(value)) throw new TypeError(`Expected an integer for i52 slot, got ${value}.`);

Type guard

function isIntegerNumber(v: unknown): v is number { return typeof v === 'number' && Number.isInteger(v); }

Prevention

When it happens

Trigger: Passing a fractional Number (e.g. 3.5) to setI52/setU52 or the exported setHeapI52/setHeapU52, or any path that converts a double to a 52-bit integer slot when the value is non-integral.

Common situations: Calling the low-level heap helpers (Module.setHeapI52/setHeapU52) with a computed float; feeding an unrounded measurement/index into a 52-bit slot; floating-point math drift producing x.0+epsilon.

Related errors


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