dotnet/runtime · error · Error

startup diagnostic client already registered

Error message

startup diagnostic client already registered

What it means

Thrown by setupJsClient when startup=true but a startup diagnostic client (startupJsClient) is already registered. The diagnostic subsystem accepts at most one startup client because there is a single startup handshake with the runtime.

Source

Thrown at src/native/libs/System.Native.Browser/diagnostics/diagnostic-server-js.ts:154

            downloadBlob(this.messagesToSend);
        }
        this.messagesToSend = [];
        return 0;
    }
}

export function initializeJsClient() {
    nextJsClient = dotnetLoaderExports.createPromiseCompletionSource<IDiagnosticClient>();
    startupJsClient = undefined;
}

export function setupJsClient(client: IDiagnosticClient, startup?: boolean) {
    if (!startup && !dotnetLoaderExports.isRuntimeRunning()) {
        throw new Error("Runtime is not running");
    }
    if (startup) {
        if (startupJsClient) {
            throw new Error("startup diagnostic client already registered");
        }
        startupJsClient = client;
    } else {
        if (nextJsClient.isDone) {
            throw new Error("multiple clients in parallel are not allowed");
        }
        nextJsClient.resolve(client);
    }
}

export function createDiagConnectionJs(socketHandle: number, scenarioName: string): DiagnosticSession {
    if (!fromScenarioNameOnce) {
        fromScenarioNameOnce = true;
        if (scenarioName.startsWith("js://gcdump")) {
            collectGcDump({}, true);
        }
        if (scenarioName.startsWith("js://counters")) {
            collectMetrics({}, true);

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Register exactly one startup diagnostic client.
  2. Run additional collections at runtime (startup=false) after the startup capture completes.

Example fix

// before: two startup clients collide
collectMetrics({}, true);
collectGcDump({}, true); // throws
// after: one startup, the rest at runtime
await collectMetrics({}, true);
await collectGcDump(); // runtime path after startup completes
Defensive patterns

Strategy: validation

Validate before calling

// Track startup registration externally so you never double-register
let startupRegistered = false;
function registerStartupCollect(fn, opts) {
  if (startupRegistered) throw new Error('A startup diagnostic client is already registered.');
  startupRegistered = true;
  return fn(opts, true);
}

Prevention

When it happens

Trigger: Calling setupJsClient (or two collect* functions) with startup=true more than once, e.g. collectMetrics({}, true) followed by collectGcDump({}, true) at boot.

Common situations: Configuring DOTNET_DiagnosticPorts to a startup scenario AND explicitly calling a collect function with startup=true; an init helper that double-registers.

Related errors


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