diff --git a/packages/ai/core/generate-text/generate-text.ts b/packages/ai/core/generate-text/generate-text.ts index dde2b5922a64..5130eb075e61 100644 --- a/packages/ai/core/generate-text/generate-text.ts +++ b/packages/ai/core/generate-text/generate-text.ts @@ -25,6 +25,8 @@ import { import { GenerateTextResult } from './generate-text-result'; import { ToToolCallArray, parseToolCall } from './tool-call'; import { ToToolResultArray } from './tool-result'; +import { AISDKError } from '@ai-sdk/provider'; +import { ProviderError } from '../../errors/provider-error'; /** Generate a text and call tools for a given prompt using a language model. @@ -215,14 +217,27 @@ By default, it's set to 0, which will disable the feature. }), tracer, fn: async span => { - const result = await model.doGenerate({ - mode, - ...callSettings, - inputFormat: currentInputFormat, - prompt: promptMessages, - abortSignal, - headers, - }); + let result: Awaited>; + + try { + result = await model.doGenerate({ + mode, + ...callSettings, + inputFormat: currentInputFormat, + prompt: promptMessages, + abortSignal, + headers, + }); + } catch (error) { + if (error instanceof AISDKError) { + throw new ProviderError({ + provider: model.provider, + message: error.message, + cause: error, + }); + } + throw error; + } // Add response information to the span: span.setAttributes( diff --git a/packages/ai/errors/invalid-argument-error.ts b/packages/ai/errors/invalid-argument-error.ts index 6b1e596cf2f8..c0bdb8317b8f 100644 --- a/packages/ai/errors/invalid-argument-error.ts +++ b/packages/ai/errors/invalid-argument-error.ts @@ -44,6 +44,9 @@ export class InvalidArgumentError extends AISDKError { ); } + /** + * @deprecated Do not use this method. It will be removed in the next major version. + */ toJSON() { return { name: this.name, diff --git a/packages/ai/errors/provider-error.ts b/packages/ai/errors/provider-error.ts new file mode 100644 index 000000000000..6c94c6f02a87 --- /dev/null +++ b/packages/ai/errors/provider-error.ts @@ -0,0 +1,33 @@ +import { AISDKError } from '@ai-sdk/provider'; + +const name = 'AI_ProviderError'; +const marker = `vercel.ai.error.${name}`; +const symbol = Symbol.for(marker); + +export class ProviderError extends AISDKError { + private readonly [symbol] = true; // used in isInstance + + readonly provider: string; + + constructor({ + message, + cause, + provider, + }: { + message: string; + cause: unknown; + provider: string; + }) { + super({ + name, + message: `${provider} provider: ${message}`, + cause, + }); + + this.provider = provider; + } + + static isInstance(error: unknown): error is ProviderError { + return AISDKError.hasMarker(error, marker); + } +} diff --git a/packages/provider/src/errors/ai-sdk-error.ts b/packages/provider/src/errors/ai-sdk-error.ts index 1f425f799d3b..0d2c1002cede 100644 --- a/packages/provider/src/errors/ai-sdk-error.ts +++ b/packages/provider/src/errors/ai-sdk-error.ts @@ -17,6 +17,14 @@ export class AISDKError extends Error { */ readonly cause?: unknown; + /** + * The provider that caused the error. + * + * Writable so it can be set by the AI SDK function on AI SDK errors + * that are thrown by providers. + */ + provider?: string; + /** * Creates an AI SDK Error. * @@ -40,6 +48,12 @@ export class AISDKError extends Error { this.cause = cause; } + get message(): string { + return this.provider != null + ? `${this.provider} provider error: ${super.message}` + : super.message; + } + /** * Checks if the given error is an AI SDK Error. * @param {unknown} error - The error to check.