Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/comprehensive-ai-sdk-error-messages.md
Original file line number Diff line number Diff line change
@@ -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.
66 changes: 54 additions & 12 deletions packages/agents-extensions/src/aiSdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
});
}
Expand Down Expand Up @@ -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,
Expand Down
88 changes: 88 additions & 0 deletions packages/agents-extensions/test/aiSdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
});
});
});