{"record":{"id":"2cfeb362a3b46460","repo":"microsoft/aspire","slug":"callback-not-found-callbackid","errorCode":null,"errorMessage":"Callback not found: ${callbackId}","messagePattern":"Callback not found: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts","lineNumber":948,"sourceCode":"\n                clearTimeout(timeout);\n                cleanupPendingListeners();\n\n                try {\n                    const reader = new rpc.SocketMessageReader(socket);\n                    const writer = new rpc.SocketMessageWriter(socket);\n                    this.connection = rpc.createMessageConnection(reader, writer);\n\n                    this.connection.onClose(() => {\n                        this.connection = null;\n                    });\n                    this.connection.onError((err: any) => console.error('JsonRpc connection error:', err));\n\n                    // Handle callback invocations from the .NET side\n                    this.connection.onRequest('invokeCallback', async (callbackId: string, args: unknown) => {\n                        const callback = callbackRegistry.get(callbackId);\n                        if (!callback) {\n                            throw new Error(`Callback not found: ${callbackId}`);\n                        }\n                        try {\n                            // The registered wrapper handles arg unpacking and handle wrapping\n                            // Pass this client so handles can be wrapped with typed wrapper classes\n                            return await Promise.resolve(callback(args, this));\n                        } catch (error) {\n                            const message = error instanceof Error ? error.message : String(error);\n                            throw new Error(`Callback execution failed: ${message}`);\n                        }\n                    });\n\n                    socket.on('error', onConnectedSocketError);\n                    socket.on('close', onConnectedSocketClose);\n\n                    const authToken = process.env.ASPIRE_REMOTE_APPHOST_TOKEN;\n                    if (!authToken) {\n                        throw new Error('ASPIRE_REMOTE_APPHOST_TOKEN environment variable is not set.');\n                    }","sourceCodeStart":930,"sourceCodeEnd":966,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts#L930-L966","documentation":"When the .NET AppHost invokes a callback by id over JSON-RPC ('invokeCallback'), the client looks the id up in its local callback registry. If no callback is registered under that id the request handler throws this Error, which is returned to the .NET side as a failed request.","triggerScenarios":"The .NET side calls invokeCallback with a callbackId that was never registered on the client, was registered on a different AspireClient instance, or whose registration was removed/disposed before invocation.","commonSituations":"Recreating a client (or reconnecting) without re-registering callbacks; callback registered after the .NET side already dispatched; stale handle referencing a callback from a previous session; mismatched callback ids across client versions.","solutions":["Register the callback on the same AspireClient instance (and before connecting or before the .NET side dispatches) so the id exists in callbackRegistry.","Log the requested callbackId and compare with the ids returned when registering callbacks to find the mismatch.","If the client was recreated, re-register all callbacks and pass the new ids to the .NET side.","Check for version mismatches between the generated TypeScript client and the AppHost that change callback id derivation."],"exampleFix":"// before\nconst client = new AspireClient();\nclient.connect(); // callbacks never registered\n\n// after\nconst client = new AspireClient();\nclient.registerCallback('myCallbackId', async (args) => { ... });\nawait client.connect();","handlingStrategy":"validation","validationCode":"if (!callbackRegistry.has(callbackId)) {\n  console.warn(`callback '${callbackId}' not registered; re-register before connecting`);\n}","typeGuard":"function isRegistered(registry: Map<string, unknown>, id: string): boolean {\n  return registry.has(id);\n}","tryCatchPattern":"// server side of the RPC sees this error; on client:\ntry {\n  await handleRemoteInvoke(callbackId, args);\n} catch (err) {\n  if (String(err.message).startsWith('Callback not found:')) reRegisterCallbacks();\n  throw err;\n}","preventionTips":["Register all callbacks before connect() and re-register after any reconnect.","Log registered callback ids at startup for diagnosis.","Keep one client instance per session; do not rebuild clients mid-session."],"tags":["callback","json-rpc","registry","typescript"],"backgroundTag":"resource-not-found","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"}