Merge pull request #2618 from RaviTharuma/fix/extract-status-code-nested-errors

fix(runtime-fallback): extract status code from nested AI SDK errors
This commit is contained in:
acamq
2026-03-16 16:28:31 -06:00
committed by GitHub
2 changed files with 79 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { classifyErrorType, extractAutoRetrySignal, isRetryableError } from "./error-classifier"
import { classifyErrorType, extractAutoRetrySignal, extractStatusCode, isRetryableError } from "./error-classifier"
describe("runtime-fallback error classifier", () => {
test("detects cooling-down auto-retry status signals", () => {
@@ -97,3 +97,72 @@ describe("runtime-fallback error classifier", () => {
expect(signal).toBeUndefined()
})
})
describe("extractStatusCode", () => {
test("extracts numeric statusCode from top-level", () => {
expect(extractStatusCode({ statusCode: 429 })).toBe(429)
})
test("extracts numeric status from top-level", () => {
expect(extractStatusCode({ status: 503 })).toBe(503)
})
test("extracts statusCode from nested data", () => {
expect(extractStatusCode({ data: { statusCode: 500 } })).toBe(500)
})
test("extracts statusCode from nested error", () => {
expect(extractStatusCode({ error: { statusCode: 502 } })).toBe(502)
})
test("extracts statusCode from nested cause", () => {
expect(extractStatusCode({ cause: { statusCode: 504 } })).toBe(504)
})
test("skips non-numeric status and finds deeper numeric statusCode", () => {
//#given — status is a string, but error.statusCode is numeric
const error = {
status: "error",
error: { statusCode: 429 },
}
//#when
const code = extractStatusCode(error)
//#then
expect(code).toBe(429)
})
test("skips non-numeric statusCode string and finds numeric in cause", () => {
const error = {
statusCode: "UNKNOWN",
status: "failed",
cause: { statusCode: 503 },
}
expect(extractStatusCode(error)).toBe(503)
})
test("returns undefined when no numeric status exists", () => {
expect(extractStatusCode({ status: "error", message: "something broke" })).toBeUndefined()
})
test("returns undefined for null/undefined error", () => {
expect(extractStatusCode(null)).toBeUndefined()
expect(extractStatusCode(undefined)).toBeUndefined()
})
test("falls back to regex match in error message", () => {
const error = { message: "Request failed with status code 429" }
expect(extractStatusCode(error, [429, 503])).toBe(429)
})
test("prefers top-level numeric over nested numeric", () => {
const error = {
statusCode: 400,
error: { statusCode: 429 },
cause: { statusCode: 503 },
}
expect(extractStatusCode(error)).toBe(400)
})
})

View File

@@ -33,8 +33,15 @@ export function extractStatusCode(error: unknown, retryOnErrors?: number[]): num
const errorObj = error as Record<string, unknown>
const statusCode = errorObj.statusCode ?? errorObj.status ?? (errorObj.data as Record<string, unknown>)?.statusCode
if (typeof statusCode === "number") {
const statusCode = [
errorObj.statusCode,
errorObj.status,
(errorObj.data as Record<string, unknown>)?.statusCode,
(errorObj.error as Record<string, unknown>)?.statusCode,
(errorObj.cause as Record<string, unknown>)?.statusCode,
].find((code): code is number => typeof code === "number")
if (statusCode !== undefined) {
return statusCode
}