diff --git a/pkg/providers/error_classifier.go b/pkg/providers/error_classifier.go index 5563e669e..bf300b955 100644 --- a/pkg/providers/error_classifier.go +++ b/pkg/providers/error_classifier.go @@ -78,20 +78,6 @@ var ( substr("invalid request format"), } - // Model invalid/not found patterns: these are 400 errors that should be - // retriable (fallback to next model), NOT treated as format errors. - modelInvalidPatterns = []errorPattern{ - substr("not a valid model"), - substr("model not found"), - substr("model_not_found"), - substr("model not available"), - substr("does not exist"), - substr("no such model"), - substr("invalid model"), - rxp(`model.*not.*supported`), - rxp(`model.*is.*unavailable`), - rxp(`model.*is.*deprecated`), - } imageDimensionPatterns = []errorPattern{ rxp(`image dimensions exceed max`), @@ -143,17 +129,6 @@ func ClassifyError(err error, provider, model string) *FailoverError { } } - // Model invalid/not found: retriable, should fallback to another model. - // This MUST run before HTTP status classification, because 400 + "not a valid model" - // would otherwise be classified as non-retriable FailoverFormat. - if matchesAny(msg, modelInvalidPatterns) { - return &FailoverError{ - Reason: FailoverModelInvalid, - Provider: provider, - Model: model, - Wrapped: err, - } - } // Try HTTP status code extraction. if status := extractHTTPStatus(msg); status > 0 { diff --git a/pkg/providers/error_classifier_test.go b/pkg/providers/error_classifier_test.go index b9a6b035a..2a45730c2 100644 --- a/pkg/providers/error_classifier_test.go +++ b/pkg/providers/error_classifier_test.go @@ -207,26 +207,20 @@ func TestClassifyError_FormatPatterns(t *testing.T) { } } -func TestClassifyError_ModelInvalidPatterns(t *testing.T) { - patterns := []string{ - "nemotron-3-nano-30b-a3b:free is not a valid model ID", - "model not found", - "model_not_found: the requested model does not exist", - "model not available in this region", - "the model does not exist or you do not have access", - "no such model: gpt-5-turbo", - "invalid model specified", - "model llama-3-8b is not supported", - "model gpt-4o-mini is unavailable", - "model codellama is deprecated", +func TestClassifyError_Status400_ModelInvalid(t *testing.T) { + // All 400 errors should be classified as FailoverModelInvalid (retriable + warning). + // The actual error message is shown in the warning to the user. + tests := []string{ + "API request failed:\n Status: 400\n Body: nemotron-3-nano-30b-a3b:free is not a valid model ID", + "API request failed:\n Status: 400\n Body: unknown error from provider", + "API request failed:\n Status: 400\n Body: model not found", } - for _, msg := range patterns { + for _, msg := range tests { err := errors.New(msg) result := ClassifyError(err, "nvidia", "test-model") if result == nil { - t.Errorf("pattern %q: expected non-nil", msg) - continue + t.Fatalf("pattern %q: expected non-nil", msg) } if result.Reason != FailoverModelInvalid { t.Errorf("pattern %q: reason = %q, want model_invalid", msg, result.Reason) @@ -240,26 +234,6 @@ func TestClassifyError_ModelInvalidPatterns(t *testing.T) { } } -func TestClassifyError_ModelInvalid_OverridesStatus400(t *testing.T) { - // This is the exact production error: status 400 + "not a valid model". - // Before the fix, status 400 was classified as FailoverFormat (non-retriable), - // which prevented fallback to other models. - err := fmt.Errorf("API request failed:\n Status: 400\n Body: {\"error\":{\"message\":\"nemotron-3-nano-30b-a3b:free is not a valid model ID\",\"code\":400}}") - result := ClassifyError(err, "nvidia", "nemotron-3-nano-30b-a3b:free") - if result == nil { - t.Fatal("expected non-nil for model-invalid 400 error") - } - if result.Reason != FailoverModelInvalid { - t.Errorf("reason = %q, want model_invalid (should override status 400)", result.Reason) - } - if !result.IsRetriable() { - t.Error("model-invalid error should be retriable to allow fallback to next model") - } - if !result.IsModelInvalid() { - t.Error("should be classified as model invalid") - } -} - func TestClassifyError_ImageDimensionError(t *testing.T) { err := errors.New("image dimensions exceed max allowed 2048x2048") result := ClassifyError(err, "openai", "gpt-4o")