From cc420d6f34aa2cecc9ce3e755119644f2271bcba Mon Sep 17 00:00:00 2001 From: Aryan Bagade Date: Mon, 8 Dec 2025 02:48:21 -0800 Subject: [PATCH] fix(agents-extensions): improve AI SDK error messages in tracing Fixes #711 When AI SDK providers return errors, the tracing system now captures comprehensive error details including responseBody, statusCode, responseHeaders, and cause fields. Previously, only the error name and message were captured via String(error), which lost critical debugging information. --- .../comprehensive-ai-sdk-error-messages.md | 5 ++ packages/agents-extensions/src/aiSdk.ts | 66 +++++++++++--- packages/agents-extensions/test/aiSdk.test.ts | 88 +++++++++++++++++++ 3 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 .changeset/comprehensive-ai-sdk-error-messages.md diff --git a/.changeset/comprehensive-ai-sdk-error-messages.md b/.changeset/comprehensive-ai-sdk-error-messages.md new file mode 100644 index 00000000..d2523a13 --- /dev/null +++ b/.changeset/comprehensive-ai-sdk-error-messages.md @@ -0,0 +1,5 @@ +--- +'@openai/agents-extensions': patch +--- + +Improve AI SDK error messages in tracing to include comprehensive error details like responseBody, statusCode, and responseHeaders when tracing is enabled. diff --git a/packages/agents-extensions/src/aiSdk.ts b/packages/agents-extensions/src/aiSdk.ts index fce58c8d..52df3114 100644 --- a/packages/agents-extensions/src/aiSdk.ts +++ b/packages/agents-extensions/src/aiSdk.ts @@ -784,22 +784,38 @@ export class AiSdkModel implements Model { data: { error: request.tracing === true - ? String(error) - : error instanceof Error - ? error.name - : undefined, + ? { + name: error.name, + message: error.message, + // Include AI SDK specific error fields if they exist. + ...(typeof error === 'object' && error !== null + ? { + ...('responseBody' in error + ? { responseBody: (error as any).responseBody } + : {}), + ...('responseHeaders' in error + ? { + responseHeaders: (error as any) + .responseHeaders, + } + : {}), + ...('statusCode' in error + ? { statusCode: (error as any).statusCode } + : {}), + ...('cause' in error + ? { cause: (error as any).cause } + : {}), + } + : {}), + } + : error.name, }, }); } else { span.setError({ message: 'Unknown error', data: { - error: - request.tracing === true - ? String(error) - : error instanceof Error - ? error.name - : undefined, + error: request.tracing === true ? String(error) : undefined, }, }); } @@ -999,11 +1015,37 @@ export class AiSdkModel implements Model { } catch (error) { if (span) { span.setError({ - message: 'Error streaming response', + message: + error instanceof Error ? error.message : 'Error streaming response', data: { error: request.tracing === true - ? String(error) + ? error instanceof Error + ? { + name: error.name, + message: error.message, + // Include AI SDK specific error fields if they exist. + ...(typeof error === 'object' && error !== null + ? { + ...('responseBody' in error + ? { responseBody: (error as any).responseBody } + : {}), + ...('responseHeaders' in error + ? { + responseHeaders: (error as any) + .responseHeaders, + } + : {}), + ...('statusCode' in error + ? { statusCode: (error as any).statusCode } + : {}), + ...('cause' in error + ? { cause: (error as any).cause } + : {}), + } + : {}), + } + : String(error) : error instanceof Error ? error.name : undefined, diff --git a/packages/agents-extensions/test/aiSdk.test.ts b/packages/agents-extensions/test/aiSdk.test.ts index b3193dad..3c0132ea 100644 --- a/packages/agents-extensions/test/aiSdk.test.ts +++ b/packages/agents-extensions/test/aiSdk.test.ts @@ -1376,4 +1376,92 @@ describe('AiSdkModel', () => { expect(parseArguments('{"a":1,"b":"c"}')).toEqual({ a: 1, b: 'c' }); }); }); + + describe('Error handling with tracing', () => { + test('captures comprehensive AI SDK error details when tracing enabled', async () => { + // Simulate an AI SDK error with responseBody and other fields. + const aiSdkError = new Error('API call failed'); + aiSdkError.name = 'AI_APICallError'; + (aiSdkError as any).responseBody = { + error: { + message: 'Rate limit exceeded', + code: 'rate_limit_exceeded', + type: 'insufficient_quota', + }, + }; + (aiSdkError as any).responseHeaders = { + 'x-request-id': 'req_abc123', + 'retry-after': '60', + }; + (aiSdkError as any).statusCode = 429; + + const model = new AiSdkModel( + stubModel({ + async doGenerate() { + throw aiSdkError; + }, + }), + ); + + try { + await withTrace('test-trace', () => + model.getResponse({ + input: 'test input', + tools: [], + handoffs: [], + modelSettings: {}, + outputType: 'text', + tracing: true, + } as any), + ); + expect.fail('Should have thrown error'); + } catch (error: any) { + // Error should be re-thrown. + expect(error.message).toBe('API call failed'); + // Verify error has the AI SDK fields. + expect((error as any).responseBody).toBeDefined(); + expect((error as any).statusCode).toBe(429); + } + }); + + test('propagates error with AI SDK fields in streaming mode', async () => { + const aiSdkError = new Error('Stream failed'); + aiSdkError.name = 'AI_StreamError'; + (aiSdkError as any).responseBody = { + error: { message: 'Connection timeout', code: 'timeout' }, + }; + (aiSdkError as any).statusCode = 504; + + const model = new AiSdkModel( + stubModel({ + async doStream() { + throw aiSdkError; + }, + }), + ); + + try { + await withTrace('test-stream', async () => { + const iter = model.getStreamedResponse({ + input: 'test', + tools: [], + handoffs: [], + modelSettings: {}, + outputType: 'text', + tracing: true, + } as any); + + for await (const _ of iter) { + // Should not get here. + } + }); + expect.fail('Should have thrown error'); + } catch (error: any) { + expect(error.message).toBe('Stream failed'); + // Verify error has the AI SDK fields. + expect((error as any).responseBody).toBeDefined(); + expect((error as any).statusCode).toBe(504); + } + }); + }); });