@@ -51,6 +51,9 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|||||||
"connection.?lost",
|
"connection.?lost",
|
||||||
"other side closed",
|
"other side closed",
|
||||||
"fetch failed",
|
"fetch failed",
|
||||||
|
"getaddrinfo",
|
||||||
|
"ENOTFOUND",
|
||||||
|
"EAI_AGAIN",
|
||||||
"upstream.?connect",
|
"upstream.?connect",
|
||||||
"reset before headers",
|
"reset before headers",
|
||||||
"socket hang up",
|
"socket hang up",
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ const nvidiaNIMResourceExhaustedMessage = "ResourceExhausted: Worker local total
|
|||||||
const bunFetchSocketClosedMessage =
|
const bunFetchSocketClosedMessage =
|
||||||
"The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()";
|
"The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()";
|
||||||
const openAIResponsesEarlyEofMessage = "OpenAI Responses stream ended before a terminal response event";
|
const openAIResponsesEarlyEofMessage = "OpenAI Responses stream ended before a terminal response event";
|
||||||
|
const wrappedDnsLookupError =
|
||||||
|
"The pending stream has been canceled (caused by: getaddrinfo ENOTFOUND bedrock-runtime.us-east-1.amazonaws.com)";
|
||||||
|
|
||||||
describe("provider retry classification", () => {
|
describe("provider retry classification", () => {
|
||||||
it("matches explicit provider retry guidance", () => {
|
it("matches explicit provider retry guidance", () => {
|
||||||
@@ -38,6 +40,15 @@ describe("provider retry classification", () => {
|
|||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
wrappedDnsLookupError,
|
||||||
|
"connect ENOTFOUND api.example.com",
|
||||||
|
"EAI_AGAIN api.example.com",
|
||||||
|
"getaddrinfo failed for api.example.com",
|
||||||
|
])("matches DNS transport failure wording: %s", (errorMessage) => {
|
||||||
|
expect(isRetryableAssistantError(fauxAssistantMessage("", { stopReason: "error", errorMessage }))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("matches OpenAI Responses streams that end before terminal events", () => {
|
it("matches OpenAI Responses streams that end before terminal events", () => {
|
||||||
expect(
|
expect(
|
||||||
isRetryableAssistantError(
|
isRetryableAssistantError(
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { fauxAssistantMessage } from "@earendil-works/pi-ai";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { createHarness } from "../harness.ts";
|
||||||
|
|
||||||
|
const wrappedDnsLookupError =
|
||||||
|
"The pending stream has been canceled (caused by: getaddrinfo ENOTFOUND bedrock-runtime.us-east-1.amazonaws.com)";
|
||||||
|
|
||||||
|
describe("issue #6904 DNS transport failure retry", () => {
|
||||||
|
it("retries a transient DNS lookup failure", async () => {
|
||||||
|
const harness = await createHarness({ settings: { retry: { enabled: true, maxRetries: 3, baseDelayMs: 1 } } });
|
||||||
|
try {
|
||||||
|
harness.setResponses([
|
||||||
|
fauxAssistantMessage("", { stopReason: "error", errorMessage: wrappedDnsLookupError }),
|
||||||
|
fauxAssistantMessage("recovered after DNS retry"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await harness.session.prompt("test");
|
||||||
|
|
||||||
|
expect(harness.faux.state.callCount).toBe(2);
|
||||||
|
expect(harness.eventsOfType("auto_retry_start").map((event) => event.errorMessage)).toEqual([
|
||||||
|
wrappedDnsLookupError,
|
||||||
|
]);
|
||||||
|
expect(harness.eventsOfType("auto_retry_end").map((event) => event.success)).toEqual([true]);
|
||||||
|
} finally {
|
||||||
|
harness.cleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user