dotnet/runtime · critical · Error

Expected internals to be an array

Error message

Expected internals to be an array

What it means

The System.Runtime.InteropServices.JavaScript interop module's dotnetInitializeModule asserts internals is an array. This module wires up JSImport/JSCall marshaling, lazy assembly loading, websockets, and HTTP, all of which read slots from the positional InternalExchange array, so a non-array breaks the contract before any interop can be set up.

Source

Thrown at src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/index.ts:31

} from "./invoke-js";
import { bindCsFunction, getAssemblyExports } from "./invoke-cs";
import { initializeMarshalersToJs, resolveOrRejectPromise } from "./marshal-to-js";
import { initializeMarshalersToCs } from "./marshal-to-cs";
import { forceDisposeProxies, releaseCSOwnedObject } from "./gc-handles";
import { cancelPromise } from "./cancelable-promise";
import { loadLazyAssembly, loadSatelliteAssemblies } from "./lazy";
import { jsInteropState } from "./marshal";
import { initializeScheduling, abortInteropTimers } from "./scheduling";
import { wsAbort, wsClose, wsCreate, wsGetState, wsOpen, wsReceive, wsSend } from "./web-socket";
import {
    httpSupportsStreamingRequest, httpSupportsStreamingResponse, httpCreateController, httpGetResponseType,
    httpGetResponseStatus, httpAbort, httpTransformStreamWrite, httpTransformStreamClose, httpFetch,
    httpFetchStream, httpFetchBytes, httpGetResponseHeaderNames, httpGetResponseHeaderValues, httpGetResponseBytes,
    httpGetResponseLength, httpGetStreamedResponseBytes,
} from "./http";

export function dotnetInitializeModule(internals: InternalExchange): void {
    if (!Array.isArray(internals)) throw new Error("Expected internals to be an array");
    const runtimeApi = internals[InternalExchangeIndex.RuntimeAPI];
    if (typeof runtimeApi !== "object") throw new Error("Expected internals to have RuntimeAPI");

    if (runtimeApi.runtimeBuildInfo.gitHash && runtimeApi.runtimeBuildInfo.gitHash !== GitHash) {
        throw new Error(`Mismatched git hashes between loader and runtime. Loader: ${runtimeApi.runtimeBuildInfo.gitHash}, Runtime: ${GitHash}`);
    }

    const runtimeApiLocal: Partial<RuntimeAPI> = {
        getAssemblyExports,
        setModuleImports,
    };
    Object.assign(runtimeApi, runtimeApiLocal);
    Object.assign(runtimeApi.INTERNAL, {
        hasProperty,
        getTypeOfProperty,
        getProperty,
        setProperty,
        getGlobalThis,

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Forward the exact internals array produced by the runtime loader, unwrapped.
  2. Rebuild with a matching SDK so the loader and interop bundles agree on the InternalExchange array contract.
  3. Clear cached/stale bundles and redeploy all dotnet files together.

Example fix

// before
dotnetInitializeModule({ internals: arr })

// after
dotnetInitializeModule(arr)
Defensive patterns

Strategy: type-guard

Validate before calling

if (!Array.isArray(internals)) throw new TypeError("internals must be an array");
dotnetInitializeModule(internals);

Type guard

const isInternalExchange = (x): x is any[] => Array.isArray(x);

Prevention

When it happens

Trigger: dotnetInitializeModule for the interop module receives a non-array (object/undefined/null) because the host forwarded a wrapped or differently-shaped exchange instead of the raw array.

Common situations: Mismatched loader/interop bundle versions; a custom host wrapping internals in an object; partial deploy of dotnet bundles.

Related errors


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