dotnet/runtime · error · Error

multiple clients in parallel are not allowed

Error message

multiple clients in parallel are not allowed

What it means

Thrown by setupJsClient (runtime path) when nextJsClient.isDone is already true, meaning a runtime diagnostic client is already pending or active. The JS diagnostic protocol supports one client at a time per session.

Source

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

}

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);
        }
        if (scenarioName.startsWith("js://cpu-samples")) {
            collectCpuSamples({}, true);
        }
        const dotnetDiagnosticClient: FnClientProvider = (globalThis as any).dotnetDiagnosticClient;

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Await the previous collect* promise before starting a new diagnostic collection.
  2. Serialize diagnostic requests through a single-slot queue.

Example fix

// before: overlapping runtime clients
collectMetrics();
collectGcDump(); // throws: parallel not allowed
// after: await before starting the next
await collectMetrics();
await collectGcDump();
Defensive patterns

Strategy: validation

Validate before calling

// Serialize runtime diagnostic collections through a single-slot lock
let diagInProgress = null;
async function runOneAtATime(fn, opts) {
  while (diagInProgress) await diagInProgress;
  diagInProgress = fn(opts);
  try { return await diagInProgress; } finally { diagInProgress = null; }
}

Prevention

When it happens

Trigger: Calling collectMetrics()/collectGcDump()/collectCpuSamples() a second time while the first session is still open and its promise has not resolved.

Common situations: UI buttons triggering overlapping traces; forgetting to await the previous collect promise; a retry fired before the first attempt settled.

Related errors


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