{"record":{"id":"cceef0ae87311d44","repo":"microsoft/aspire","slug":"registercancellation-signal-is-ambiguous-when-multiple","errorCode":null,"errorMessage":"registerCancellation(signal) is ambiguous when multiple AspireClient instances are connected. Pass the client explicitly.","messagePattern":"registerCancellation\\(signal\\) is ambiguous when multiple AspireClient instances are connected\\. Pass the client explicitly\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts","lineNumber":581,"sourceCode":"const connectedClients = new Set<AspireClient>();\n\nfunction resolveCancellationClient(client?: AspireClientRpc): AspireClientRpc {\n    if (client) {\n        return client;\n    }\n\n    if (connectedClients.size === 1) {\n        return connectedClients.values().next().value as AspireClient;\n    }\n\n    if (connectedClients.size === 0) {\n        throw new Error(\n            'registerCancellation(signal) requires a connected AspireClient. ' +\n            'Pass the client explicitly or connect the client first.'\n        );\n    }\n\n    throw new Error(\n        'registerCancellation(signal) is ambiguous when multiple AspireClient instances are connected. ' +\n        'Pass the client explicitly.'\n    );\n}\n\nfunction isAspireClientLike(value: unknown): value is AspireClientRpc {\n    if (!value || typeof value !== 'object') {\n        return false;\n    }\n\n    const candidate = value as {\n        invokeCapability?: unknown;\n        cancelToken?: unknown;\n        connected?: unknown;\n    };\n\n    return typeof candidate.invokeCapability === 'function'\n        && typeof candidate.cancelToken === 'function'","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts#L563-L599","documentation":"When multiple AspireClient instances are connected simultaneously, registerCancellation(signal) cannot determine which client should own the cancellation registration, so it refuses to guess and throws. This prevents cancellation signals from silently binding to the wrong client.","triggerScenarios":"Calling registerCancellation(signal) with no explicit client while two or more AspireClients are connected (e.g. an app host client plus a dashboard/debug client); leaving a previous test's client connected while opening a new one.","commonSituations":"Test suites that share a process across cases without disconnecting clients; apps that connect to multiple environments concurrently; refactors that added a second client but not explicit registration targets.","solutions":["Pass the client explicitly: registerCancellation(signal, client).","Disconnect (client.close()/disconnect()) stale clients so only one remains connected.","Scope clients per test/module and always clean up in teardown.","Track connected clients in a registry to make the intended target explicit."],"exampleFix":"// before\nregisterCancellation(signal); // ambiguous: 2 clients connected\n\n// after\nregisterCancellation(signal, appHostClient); // explicit target","handlingStrategy":"validation","validationCode":"// Ensure only one connected client, or always pass one explicitly\nregisterCancellation(signal, appHostClient); // explicit target avoids ambiguity","typeGuard":"function hasSingleConnectedClient(clients: AspireClient[]): AspireClient | null {\n  const connected = clients.filter(c => c.connected);\n  return connected.length === 1 ? connected[0] : null;\n}","tryCatchPattern":"try {\n  registerCancellation(signal);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"is ambiguous\")) {\n    registerCancellation(signal, intendedClient); // disambiguate\n  } else throw e;\n}","preventionTips":["Always pass the client explicitly when more than one client may exist.","Disconnect stale clients in test teardown and shutdown paths.","Keep a registry of connected clients so the intended registration target is unambiguous."],"tags":["cancellation","ambiguity","typescript","connection"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}