From 5b39be3653ac22a27d79a3359363eeae9f3acddc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 23 Apr 2026 07:43:43 +0100 Subject: [PATCH] fix(agents): preserve raw fallback schema errors --- src/agents/model-fallback.test.ts | 44 +++++++++++++++++++++++++++++++ src/agents/model-fallback.ts | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/agents/model-fallback.test.ts b/src/agents/model-fallback.test.ts index 9afc22e60cd..95f31bcde6b 100644 --- a/src/agents/model-fallback.test.ts +++ b/src/agents/model-fallback.test.ts @@ -6,6 +6,7 @@ import { resetLogger, setLoggerOverride } from "../logging/logger.js"; import { createWarnLogCapture } from "../logging/test-helpers/warn-log-capture.js"; import { AUTH_STORE_VERSION } from "./auth-profiles/constants.js"; import type { AuthProfileStore } from "./auth-profiles/types.js"; +import { FailoverError } from "./failover-error.js"; import { LiveSessionModelSwitchError } from "./live-model-switch-error.js"; import { runWithImageModelFallback, runWithModelFallback } from "./model-fallback.js"; import { makeModelFallbackCfg } from "./test-helpers/model-fallback-config-fixture.js"; @@ -372,6 +373,49 @@ describe("runWithModelFallback", () => { expect(result.attempts[0].reason).toBe("unknown"); }); + it("keeps raw provider schema errors in fallback summaries", async () => { + const cfg = makeCfg({ + agents: { + defaults: { + model: { + primary: "openai/gpt-5.4", + fallbacks: ["openai/gpt-5.4-mini"], + }, + }, + }, + }); + const rawError = + "400 The following tools cannot be used with reasoning.effort 'minimal': web_search."; + const run = vi.fn().mockRejectedValue( + new FailoverError("LLM request failed: provider rejected the request schema.", { + provider: "openai", + model: "gpt-5.4", + reason: "format", + status: 400, + rawError, + }), + ); + + await expect( + runWithModelFallback({ + cfg, + provider: "openai", + model: "gpt-5.4", + run, + }), + ).rejects.toMatchObject({ + name: "FallbackSummaryError", + message: expect.stringContaining(rawError), + attempts: expect.arrayContaining([ + expect.objectContaining({ + error: rawError, + reason: "format", + status: 400, + }), + ]), + }); + }); + it("passes original unknown errors to onError during fallback", async () => { const cfg = makeCfg(); const unknownError = new Error("provider misbehaved"); diff --git a/src/agents/model-fallback.ts b/src/agents/model-fallback.ts index c676acf4631..f6e14135fa5 100644 --- a/src/agents/model-fallback.ts +++ b/src/agents/model-fallback.ts @@ -239,7 +239,7 @@ function recordFailedCandidateAttempt(params: { params.attempts.push({ provider: params.candidate.provider, model: params.candidate.model, - error: described.message, + error: described.rawError ?? described.message, reason: described.reason ?? "unknown", status: described.status, code: described.code,