dotnet/runtime · critical · Error

Expected internals to have RuntimeAPI

Error message

Expected internals to have RuntimeAPI

What it means

Thrown by dotnetInitializeModule (diagnostics module) when internals[RuntimeAPI] is not an object. The diagnostics module needs the RuntimeAPI slot to read config, register diagnostics exports, and attach to git-hash/build info; its absence means the internals array was not fully populated.

Source

Thrown at src/native/libs/System.Native.Browser/diagnostics/index.ts:23

import { InternalExchangeIndex } from "../types";

import GitHash from "consts:gitHash";

import { dotnetApi, dotnetUpdateInternals, dotnetUpdateInternalsSubscriber } from "./cross-module";
import { registerExit } from "./exit";
import { installNativeSymbols, symbolicateStackTrace } from "./symbolicate";
import { installLoggingProxy } from "./console-proxy";
import { collectMetrics } from "./dotnet-counters";
import { collectGcDump } from "./dotnet-gcdump";
import { collectCpuSamples } from "./dotnet-cpu-profiler";
import { connectDSRouter, ds_rt_websocket_close, ds_rt_websocket_create, ds_rt_websocket_poll, ds_rt_websocket_recv, ds_rt_websocket_send, initializeDS } from "./diagnostic-server";
import { ds_rt_browser_performance_measure } from "./browser-profiler";

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}, Diagnostics: ${GitHash}`);
    }

    internals[InternalExchangeIndex.DiagnosticsExportsTable] = diagnosticsExportsToTable({
        symbolicateStackTrace,
        installNativeSymbols,
        ds_rt_websocket_create,
        ds_rt_websocket_send,
        ds_rt_websocket_poll,
        ds_rt_websocket_recv,
        ds_rt_websocket_close,
        ds_rt_browser_performance_measure,
    });
    dotnetUpdateInternals(internals, dotnetUpdateInternalsSubscriber);

    registerExit();

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Let the loader sequence module initialization so RuntimeAPI is populated before the diagnostics module initializes.
  2. Keep loader, runtime, and diagnostics module at the same build version so the InternalExchangeIndex slots line up.
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the RuntimeAPI slot is populated before diagnostics init
const runtimeApi = internals[InternalExchangeIndex.RuntimeAPI];
if (typeof runtimeApi !== 'object' || runtimeApi === null) {
  throw new Error('RuntimeAPI missing from internals; initialize the runtime before the diagnostics module.');
}

Type guard

function hasRuntimeApi(internals: unknown): internals is InternalExchange {
  return Array.isArray(internals)
    && typeof internals[InternalExchangeIndex.RuntimeAPI] === 'object'
    && internals[InternalExchangeIndex.RuntimeAPI] !== null;
}

Prevention

When it happens

Trigger: Diagnostics init runs before the runtime has placed its RuntimeAPI object into the internals exchange, or internals has fewer slots than expected.

Common situations: Init ordering broken (diagnostics initialized before runtime slot populated); version mismatch reducing the internals array length; partial/aborted loader init.

Related errors


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