Skip to content

Commit c83f1ea

Browse files
author
secooond
committed
fix(failover): classify upstream_error errorType as server_error for model fallback
When a provider returns an error with type:upstream_error (e.g. OpenAI responses API), classifyFailoverSignal returns null because the errorType is not recognized, so model fallback never triggers and the turn ends with 'LLM request failed.' Add upstream_error → server_error classification in classifyFailoverSignal so configured fallback models are attempted on transient provider-side failures. Closes #95519.
1 parent 7626ca3 commit c83f1ea

2 files changed

Lines changed: 38 additions & 0 deletions

File tree

src/agents/embedded-agent-helpers/errors.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ describe("formatAssistantErrorText streaming JSON parse classification", () => {
9898
});
9999
});
100100

101+
import { classifyFailoverSignal } from "./errors.js";
102+
101103
describe("isLikelyContextOverflowError", () => {
102104
it("detects Codex promptError wording for a full context window", () => {
103105
expect(
@@ -107,3 +109,28 @@ describe("isLikelyContextOverflowError", () => {
107109
).toBe(true);
108110
});
109111
});
112+
113+
describe("classifyFailoverSignal upstream_error", () => {
114+
it("classifies upstream_error errorType as server_error for model fallback (regression for #95519)", () => {
115+
const classification = classifyFailoverSignal({
116+
message: '{"error":{"type":"upstream_error","message":"Upstream request failed"}}',
117+
errorType: "upstream_error",
118+
code: null as unknown as undefined,
119+
provider: "openai",
120+
});
121+
expect(classification).toEqual({
122+
kind: "reason",
123+
reason: "server_error",
124+
});
125+
});
126+
127+
it("does not classify unhandled error messages as server_error", () => {
128+
const classification = classifyFailoverSignal({
129+
message: "Some other error",
130+
provider: "openai",
131+
});
132+
// Without errorType, upstream_error, or other recognized patterns,
133+
// classification falls through and returns null.
134+
expect(classification).toBeNull();
135+
});
136+
});

src/agents/embedded-agent-helpers/errors.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,17 @@ export function classifyFailoverSignal(signal: FailoverSignal): FailoverClassifi
11761176
errorType: signal.errorType,
11771177
})
11781178
: null;
1179+
// upstream_error is a provider-side transient failure (e.g. OpenAI
1180+
// responses API returns {"error":{"type":"upstream_error"}}). Classify
1181+
// it as server_error so model fallback triggers instead of ending the
1182+
// turn with "LLM request failed." (issue #95519).
1183+
if (
1184+
!providerPluginReason &&
1185+
signal.errorType != null &&
1186+
normalizeLowercaseStringOrEmpty(signal.errorType) === "upstream_error"
1187+
) {
1188+
return toReasonClassification("server_error");
1189+
}
11791190
const effectiveMessageClassification = providerPluginReason
11801191
? toReasonClassification(providerPluginReason)
11811192
: mergeMessageAndDetailClassification(messageClassification, detailClassification);

0 commit comments

Comments
 (0)