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
- Await the previous collector's returned promise (which resolves on session close) before starting another.
- Serialize diagnostic scenarios in UI handlers: disable the button until the prior run resolves.
- 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
- Serialize diagnostic scenarios; await the prior run's promise before starting another.
- Disable UI controls that trigger collectors while one is running.
- In tests, run collectors sequentially in one async function.
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
- Diag module already loaded
- No active JS diagnostic session
- No active JS diagnostic session
- No active JS diagnostic session
- No active session to reconnect
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)