{"record":{"id":"25be4dff846b07a5","repo":"microsoft/aspire","slug":"registercancellation-signal-requires-a-connected","errorCode":null,"errorMessage":"registerCancellation(signal) requires a connected AspireClient. Pass the client explicitly or connect the client first.","messagePattern":"registerCancellation\\(signal\\) requires a connected AspireClient\\. Pass the client explicitly or connect the client first\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts","lineNumber":575,"sourceCode":"/**\n * Registry for cancellation tokens.\n * Maps cancellation IDs to cleanup functions.\n */\nconst cancellationRegistry = new Map<string, () => void>();\nlet cancellationIdCounter = 0;\nconst 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;","sourceCodeStart":557,"sourceCodeEnd":593,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts#L557-L593","documentation":"resolveCancellationClient picks the AspireClient to use for registerCancellation(signal). With zero connected clients there is nothing to register cancellation against, so it throws and asks the caller to either pass the client explicitly or connect one first.","triggerScenarios":"Calling registerCancellation(signal) before any AspireClient.connect() has completed; the client disconnected before registration; forgetting to pass an explicit client in a context with no ambient connection.","commonSituations":"Setting up cancellation at module top level before the connection is established; connection failure earlier in startup leaving zero clients; tests that never connect a client.","solutions":["Pass the AspireClient explicitly: registerCancellation(signal, client).","Await client.connect() (and confirm it succeeded) before calling registerCancellation.","Re-check connection lifecycle ordering in startup code.","In tests, create and connect a client (or fake server) before registering cancellation."],"exampleFix":"// before\nregisterCancellation(abortController.signal); // no client connected\n\n// after\nconst client = new AspireClient();\nawait client.connect();\nregisterCancellation(abortController.signal, client);","handlingStrategy":"validation","validationCode":"if (!client || !client.isConnected?.()) {\n  throw new Error(\"Connect an AspireClient before registerCancellation, or pass the client explicitly\");\n}\nregisterCancellation(signal, client);","typeGuard":"function isConnectedClient(c: unknown): c is AspireClient {\n  return c instanceof AspireClient && (c as { connected?: boolean }).connected === true;\n}","tryCatchPattern":"try {\n  registerCancellation(signal);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"requires a connected AspireClient\")) {\n    await client.connect();\n    registerCancellation(signal, client);\n  } else throw e;\n}","preventionTips":["Establish the client connection during startup, before cancellation setup.","Always pass the client explicitly rather than relying on ambient state.","Verify connect() succeeded before proceeding."],"tags":["cancellation","connection","typescript"],"backgroundTag":"connection-required","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"}