From 4946a8b449a963cbe04c1719f88f07d95f692f29 Mon Sep 17 00:00:00 2001 From: qs3c <2749950753@qq.com> Date: Wed, 4 Mar 2026 17:50:46 +0800 Subject: [PATCH] fix(openai_compat): clarify HTML response errors --- pkg/providers/openai_compat/provider.go | 48 +++++++++++++++++++- pkg/providers/openai_compat/provider_test.go | 21 +++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index ff9109e96..621e34a89 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -192,7 +192,53 @@ func (p *Provider) Chat( return nil, fmt.Errorf("API request failed:\n Status: %d\n Body: %s", resp.StatusCode, string(body)) } - return parseResponse(body) + out, err := parseResponse(body) + if err != nil { + return nil, wrapResponseParseError(err, body, resp.Header.Get("Content-Type"), p.apiBase) + } + + return out, nil +} + +func wrapResponseParseError(err error, body []byte, contentType, apiBase string) error { + trimmedContentType := strings.TrimSpace(contentType) + if looksLikeHTML(body, trimmedContentType) { + contentTypeHint := "" + if trimmedContentType != "" { + contentTypeHint = fmt.Sprintf(" (content-type: %s)", trimmedContentType) + } + return fmt.Errorf( + "expected JSON response from %s/chat/completions, but received HTML%s; check api_base or proxy configuration. Response preview: %s", + apiBase, + contentTypeHint, + responsePreview(body, 160), + ) + } + return err +} + +func looksLikeHTML(body []byte, contentType string) bool { + contentType = strings.ToLower(strings.TrimSpace(contentType)) + if strings.Contains(contentType, "text/html") || strings.Contains(contentType, "application/xhtml+xml") { + return true + } + + trimmed := strings.ToLower(strings.TrimSpace(string(body))) + return strings.HasPrefix(trimmed, "" + } + if len(preview) <= max { + return preview + } + return preview[:max] + "..." } func parseResponse(body []byte) (*LLMResponse, error) { diff --git a/pkg/providers/openai_compat/provider_test.go b/pkg/providers/openai_compat/provider_test.go index 174bcf00d..244a20672 100644 --- a/pkg/providers/openai_compat/provider_test.go +++ b/pkg/providers/openai_compat/provider_test.go @@ -212,6 +212,27 @@ func TestProviderChat_HTTPError(t *testing.T) { } } +func TestProviderChat_HTMLSuccessResponseReturnsHelpfulError(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("gateway login")) + })) + defer server.Close() + + p := NewProvider("key", server.URL, "") + _, err := p.Chat(t.Context(), []Message{{Role: "user", Content: "hi"}}, nil, "gpt-4o", nil) + if err == nil { + t.Fatal("expected error, got nil") + } + if !strings.Contains(err.Error(), "received HTML") { + t.Fatalf("expected helpful HTML error, got %v", err) + } + if !strings.Contains(err.Error(), "check api_base or proxy configuration") { + t.Fatalf("expected configuration hint, got %v", err) + } +} + func TestProviderChat_StripsMoonshotPrefixAndNormalizesKimiTemperature(t *testing.T) { var requestBody map[string]any