{"record":{"id":"3372103c776ae169","repo":"dotnet/runtime","slug":"multiple-clients-in-parallel-are-not-allowed","errorCode":null,"errorMessage":"multiple clients in parallel are not allowed","messagePattern":"multiple clients in parallel are not allowed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mono/browser/runtime/diagnostics/diagnostics-js.ts","lineNumber":144,"sourceCode":"        }\n        if (this.messagesToSend.length === 0) {\n            return 0;\n        }\n        if (this.diagClient && !this.diagClient.skipDownload) {\n            downloadBlob(this.messagesToSend);\n        }\n        this.messagesToSend = [];\n        return 0;\n    }\n}\n\nexport function cleanupClient () {\n    nextJsClient = loaderHelpers.createPromiseController<IDiagnosticClient>();\n}\n\nexport function setupJsClient (client:IDiagnosticClient) {\n    if (nextJsClient.promise_control.isDone) {\n        throw new Error(\"multiple clients in parallel are not allowed\");\n    }\n    nextJsClient.promise_control.resolve(client);\n}\n\nexport function createDiagConnectionJs (socket_handle:number, scenarioName:string):DiagnosticSession {\n    if (!fromScenarioNameOnce) {\n        fromScenarioNameOnce = true;\n        if (scenarioName.startsWith(\"js://gcdump\")) {\n            collectGcDump({});\n        }\n        if (scenarioName.startsWith(\"js://counters\")) {\n            collectMetrics({});\n        }\n        if (scenarioName.startsWith(\"js://cpu-samples\")) {\n            collectCpuSamples({});\n        }\n        const dotnetDiagnosticClient:fnClientProvider = (globalThis as any).dotnetDiagnosticClient;\n        if (typeof dotnetDiagnosticClient === \"function\" ) {","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/src/mono/browser/runtime/diagnostics/diagnostics-js.ts#L126-L162","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: two collectors at once\nconst a = collectMetrics({ durationSeconds: 10 });\nconst b = collectGcDump({}); // throws 'multiple clients in parallel are not allowed'\n\n// after: serialize\nawait collectMetrics({ durationSeconds: 10 });\nawait collectGcDump({});","handlingStrategy":"validation","validationCode":"let diagInFlight = false;\nasync function runDiag (fn, opts) {\n  if (diagInFlight) throw new Error('a diagnostic run is already in progress');\n  diagInFlight = true;\n  try { return await fn(opts); } finally { diagInFlight = false; }\n}","typeGuard":"function canRegisterDiagClient (nextJsClient): boolean {\n  return !nextJsClient.promise_control.isDone;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["diagnostics","session-management","singleton","typescript"],"backgroundTag":null,"analyzedSha":"60108ba66eb7d1d12f595480091b4ad80a24b172","analyzedAt":"2026-08-10T18:54:11.478Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}