mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
fix(core): read provider errors from plain AI SDK stream errors (#51194)
This commit is contained in:
@@ -80,7 +80,13 @@ const SERVER_CODES = new Set([
|
||||
"slow_down",
|
||||
"serviceunavailableexception",
|
||||
])
|
||||
const INVALID_REQUEST_CODES = new Set(["invalid_prompt", "invalid_request_error", "validationexception"])
|
||||
// `invalid_request` is the Vercel AI Gateway's code for an upstream request rejection.
|
||||
const INVALID_REQUEST_CODES = new Set([
|
||||
"invalid_prompt",
|
||||
"invalid_request",
|
||||
"invalid_request_error",
|
||||
"validationexception",
|
||||
])
|
||||
// Azure OpenAI reports `content_filter` with `innererror.code` ResponsibleAIPolicyViolation.
|
||||
// OpenRouter tags provider failures with a typed `error_type`; its Responses skin also
|
||||
// emits `image_content_policy_violation` as the native code.
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
ProviderMetadata,
|
||||
TransportError,
|
||||
ToolResultValue,
|
||||
UnknownProviderError,
|
||||
type ContentPart,
|
||||
type LLMRequest,
|
||||
type Media,
|
||||
@@ -936,15 +935,18 @@ function llmError(error: unknown, operation: "request" | "read") {
|
||||
code: network.code,
|
||||
}),
|
||||
})
|
||||
return new AIError({
|
||||
reason: new UnknownProviderError({
|
||||
message: unknownErrorMessage(error),
|
||||
body: errorBody(error),
|
||||
cause: error,
|
||||
}),
|
||||
return RequestExecutor.httpFailure({
|
||||
message: unknownErrorMessage(error),
|
||||
data: errorValue(error) ?? error,
|
||||
responseBody: errorBody(error),
|
||||
cause: error,
|
||||
})
|
||||
}
|
||||
|
||||
// AI SDK stream errors can arrive as plain objects. A gateway's type validation error keeps the provider's error
|
||||
// response in `value`, which carries the message and codes.
|
||||
const errorValue = (error: unknown) => (ProviderShared.isRecord(error) ? error.value : undefined)
|
||||
|
||||
// Runtime-generated network failure shapes. The codes mirror the AI SDK's own
|
||||
// Bun network error list in handleFetchError; the messages are undici's fetch
|
||||
// TypeError and stream termination strings plus our SSE chunk timeout error.
|
||||
@@ -1032,7 +1034,15 @@ const decodeProviderError = Schema.decodeUnknownOption(
|
||||
)
|
||||
|
||||
function unknownErrorMessage(error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
const message =
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: typeof error === "string"
|
||||
? error
|
||||
: ([error, errorValue(error)]
|
||||
.map((value) => Option.getOrUndefined(decodeProviderError(value)))
|
||||
.flatMap((decoded) => [decoded?.error?.message, decoded?.message])
|
||||
.find((value) => value?.trim()) ?? "")
|
||||
return message.trim() === "" ? "Provider request failed" : message
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
CompactionPart,
|
||||
ProviderID,
|
||||
HttpContext,
|
||||
InvalidRequestError,
|
||||
LLMEvent,
|
||||
Message,
|
||||
RateLimitError,
|
||||
@@ -883,6 +884,23 @@ Object.values({
|
||||
)
|
||||
})
|
||||
|
||||
// Shapes the Vercel AI Gateway streams when the upstream rejects a request.
|
||||
Object.entries({
|
||||
"type validation": {
|
||||
name: "AI_TypeValidationError",
|
||||
value: { error: { type: "invalid_request_error", message: "Bad max_tokens" } },
|
||||
},
|
||||
invalid_request: { code: "invalid_request", message: "Bad max_tokens" },
|
||||
}).forEach(([shape, failure]) =>
|
||||
it.effect(`reads gateway ${shape} stream errors as invalid requests`, () =>
|
||||
Effect.gen(function* () {
|
||||
const error = yield* streamFailure(failure, true)
|
||||
expect(error.message).toBe("Bad max_tokens")
|
||||
expect(error.reason).toBeInstanceOf(InvalidRequestError)
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
it.effect("does not copy Error request internals into the provider body", () =>
|
||||
Effect.gen(function* () {
|
||||
const cause = Object.assign(new Error("Connection failed"), {
|
||||
|
||||
Reference in New Issue
Block a user