{"record":{"id":"a97e252d9db96f40","repo":"microsoft/aspire","slug":"callback-execution-failed-message","errorCode":null,"errorMessage":"Callback execution failed: ${message}","messagePattern":"Callback execution failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts","lineNumber":956,"sourceCode":"\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                    }\n                    this.connection.listen();\n                    const authenticated = await this.connection.sendRequest<boolean>('authenticate', authToken);\n                    if (!authenticated) {\n                        throw new Error('Failed to authenticate to the AppHost server.');\n                    }\n\n                    connectedClients.add(this);\n                    this._connectPromise = null;","sourceCodeStart":938,"sourceCodeEnd":974,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts#L938-L974","documentation":"The invokeCallback request handler wraps the user callback invocation in try-catch; if the callback itself throws (or its returned promise rejects), the error message is captured and rethrown as 'Callback execution failed: <message>'. It preserves the original message but not the original stack/type.","triggerScenarios":"Any user-registered callback invoked by the .NET side throws synchronously or rejects: unhandled null arguments, failed deserialize of args, thrown application logic, or an inner await rejecting inside the callback body.","commonSituations":"Callback logic assumes a field exists in args; database/network call inside callback fails; bug in wrapper-generated unpacking; error thrown by a nested handle method invoked inside the callback.","solutions":["Read the appended <message> portion to identify the original failure and fix the callback code that threw.","Add defensive validation of the args object inside the callback before using nested properties.","Wrap risky operations inside the callback so full stack traces and custom error types are preserved/logged.","If the message indicates a transport-level failure, verify the connection stayed open during callback execution."],"exampleFix":"// before\nclient.registerCallback('id', async (args) => {\n  return JSON.parse(args.payload).value; // throws if payload is undefined\n});\n\n// after\nclient.registerCallback('id', async (args) => {\n  if (!args?.payload) throw new Error(`payload missing for callback 'id'`);\n  return JSON.parse(args.payload).value;\n});","handlingStrategy":"try-catch","validationCode":"if (!args || typeof args !== 'object') {\n  throw new Error(`invalid callback args: ${JSON.stringify(args)}`);\n}","typeGuard":"function hasPayload(args: unknown): args is { payload: string } {\n  return typeof args === 'object' && args !== null && 'payload' in args;\n}","tryCatchPattern":"try {\n  return await callback(args, client);\n} catch (error) {\n  console.error('callback failed:', error); // preserve original before wrapping\n  throw error;\n}","preventionTips":["Validate args shape at the top of every callback.","Avoid throwing inside callbacks for control flow; return structured error results when possible.","Log errors inside callbacks so full stacks survive the message-only wrapper."],"tags":["callback","error-handling","typescript"],"backgroundTag":"callback-execution-failed","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"}