fix(runtime-fallback): prefer numeric status codes over non-numeric in extraction chain

The nullish-coalescing chain could stop at a non-numeric value (e.g.
status: "error"), preventing deeper nested numeric statusCode values
from being reached. Switch to Array.find() with a type guard to always
select the first numeric value.

Adds 11 tests for extractStatusCode covering: top-level, nested
(data/error/cause), non-numeric skip, fallback to regex, and
precedence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ravi Tharuma
2026-03-16 13:51:23 +01:00
parent 24a0f7b032
commit de66f1f397
2 changed files with 79 additions and 3 deletions
@@ -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)
})
})
@@ -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 ?? (errorObj.error as Record<string, unknown>)?.statusCode ?? (errorObj.cause 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
}