From f7270a0f9757be029b6c943f55d3b2a185111aa3 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 1 May 2026 01:25:01 +0900 Subject: [PATCH] fix(model-error-classifier): scope forbidden-provider retry to specific phrase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bare "403" and "forbidden" substring patterns (added in 034744cb to retry the "Selected provider is forbidden" case from PR #3706) matched any error message containing those tokens — tool-level 403s, file-perm "forbidden" messages, unrelated upstream errors that happened to spell "forbidden". The legacy model-fallback path then armed setPendingModelFallback on those unrelated errors, jumping Sisyphus to claude-opus-4-7 (first entry of its fallback chain) regardless of the user's configured model. Replace the bare patterns with the specific phrases PR #3706 actually targeted, and add regression tests asserting unrelated 403/forbidden messages stay non-retryable. Reported-by: ilove_borshch on Discord (#omo-help) --- src/shared/model-error-classifier.test.ts | 22 ++++++++++++++++++++++ src/shared/model-error-classifier.ts | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/shared/model-error-classifier.test.ts b/src/shared/model-error-classifier.test.ts index c4989d199..c7f41ef4e 100644 --- a/src/shared/model-error-classifier.test.ts +++ b/src/shared/model-error-classifier.test.ts @@ -248,6 +248,28 @@ describe("model-error-classifier", () => { //#then expect(result).toBe(true) }) + + test("does not treat unrelated forbidden messages as retryable", () => { + //#given + const error = { message: "EACCES: forbidden write to /etc/hosts" } + + //#when + const result = shouldRetryError(error) + + //#then + expect(result).toBe(false) + }) + + test("does not treat unrelated 403 messages as retryable", () => { + //#given + const error = { message: "Tool returned HTTP 403 for the requested URL" } + + //#when + const result = shouldRetryError(error) + + //#then + expect(result).toBe(false) + }) }) export {} diff --git a/src/shared/model-error-classifier.ts b/src/shared/model-error-classifier.ts index 611a71aac..2c74d0831 100644 --- a/src/shared/model-error-classifier.ts +++ b/src/shared/model-error-classifier.ts @@ -72,8 +72,8 @@ const RETRYABLE_MESSAGE_PATTERNS = [ "504", "429", "529", - "403", - "forbidden", + "selected provider is forbidden", + "provider is forbidden", ] /**