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` in the diagnostics JS client when a second JS diagnostic client is registered while a previous one is still pending/active. The diagnostics subsystem is a singleton: only one JS-side client (counters, gcdump, or cpu samples) can drive the session at a time, because it consumes the single `serverSession`.

Solutions

  1. Await the previous collector's returned promise (which resolves on session close) before starting another.
  2. Serialize diagnostic scenarios in UI handlers: disable the button until the prior run resolves.
  3. If you need both, run them sequentially in one async function rather than in parallel.

Example fix

// before: two collectors at once
const a = collectMetrics({ durationSeconds: 10 });
const b = collectGcDump({}); // throws 'multiple clients in parallel are not allowed'

// after: serialize
await collectMetrics({ durationSeconds: 10 });
await collectGcDump({});
Defensive patterns

Strategy: validation

Validate before calling

let diagInFlight = false;
async function runDiag (fn, opts) {
  if (diagInFlight) throw new Error('a diagnostic run is already in progress');
  diagInFlight = true;
  try { return await fn(opts); } finally { diagInFlight = false; }
}

Type guard

function canRegisterDiagClient (nextJsClient): boolean {
  return !nextJsClient.promise_control.isDone;
}

Prevention

When it happens

Trigger: Calling `setupJsClient(client)` (diagnostics-js.ts:142) when `nextJsClient.promise_control.isDone` is true — i.e. a previous call already resolved the pending client. Concretely: calling `collectMetrics` and `collectGcDump` (or two `collectMetrics`) in parallel without waiting for the first session to close.

Common situations: Firing two diagnostic buttons in a UI without disabling the first; chaining `collectGcDump` immediately after `collectMetrics` without awaiting close; tests that invoke multiple collectors concurrently.

Related errors


AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10). Data as JSON: /api/errors/3372103c776ae169. Report an issue: GitHub.

Appendix: source

Thrown at src/mono/browser/runtime/diagnostics/diagnostics-js.ts:144

        }
        if (this.messagesToSend.length === 0) {
            return 0;
        }
        if (this.diagClient && !this.diagClient.skipDownload) {
            downloadBlob(this.messagesToSend);
        }
        this.messagesToSend = [];
        return 0;
    }
}

export function cleanupClient () {
    nextJsClient = loaderHelpers.createPromiseController<IDiagnosticClient>();
}

export function setupJsClient (client:IDiagnosticClient) {
    if (nextJsClient.promise_control.isDone) {
        throw new Error("multiple clients in parallel are not allowed");
    }
    nextJsClient.promise_control.resolve(client);
}

export function createDiagConnectionJs (socket_handle:number, scenarioName:string):DiagnosticSession {
    if (!fromScenarioNameOnce) {
        fromScenarioNameOnce = true;
        if (scenarioName.startsWith("js://gcdump")) {
            collectGcDump({});
        }
        if (scenarioName.startsWith("js://counters")) {
            collectMetrics({});
        }
        if (scenarioName.startsWith("js://cpu-samples")) {
            collectCpuSamples({});
        }
        const dotnetDiagnosticClient:fnClientProvider = (globalThis as any).dotnetDiagnosticClient;
        if (typeof dotnetDiagnosticClient === "function" ) {

View on GitHub (pinned to 60108ba66e)