Redact provider error bodies

This commit is contained in:
2026-07-05 00:20:26 +00:00
parent 1b39f82117
commit 6742def4d3
3 changed files with 10 additions and 6 deletions

View File

@@ -177,7 +177,7 @@ Malformed responses return `ErrMalformedResponse`.
## Error Handling ## Error Handling
- network/request-construction failures: `ErrRequestFailed` - network/request-construction failures: `ErrRequestFailed`
- non-2xx HTTP status: `ErrUnexpectedStatus` (includes status code and trimmed response body snippet) - non-2xx HTTP status: `ErrUnexpectedStatus` (includes status code; provider response bodies are not included)
- malformed response shape/content: `ErrMalformedResponse` - malformed response shape/content: `ErrMalformedResponse`
## Unsupported Or Non-Serialized Fields ## Unsupported Or Non-Serialized Fields

View File

@@ -139,8 +139,8 @@ func (c *OpenAICompatibleClient) Generate(ctx context.Context, req domain.Genera
defer httpResp.Body.Close() defer httpResp.Body.Close()
if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
body, _ := io.ReadAll(io.LimitReader(httpResp.Body, 4096)) _, _ = io.Copy(io.Discard, io.LimitReader(httpResp.Body, 4096))
return nil, fmt.Errorf("%w: status=%d body=%q", ErrUnexpectedStatus, httpResp.StatusCode, strings.TrimSpace(string(body))) return nil, fmt.Errorf("%w: status=%d", ErrUnexpectedStatus, httpResp.StatusCode)
} }
var wireResp openAIChatResponse var wireResp openAIChatResponse

View File

@@ -903,9 +903,10 @@ func TestOpenAICompatibleClientEndpointOverride(t *testing.T) {
} }
func TestOpenAICompatibleClientNon2xxError(t *testing.T) { func TestOpenAICompatibleClientNon2xxError(t *testing.T) {
const sensitiveBody = `provider-secret-fragment request_payload_details`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(`{"error":"bad request payload"}`)) _, _ = w.Write([]byte(`{"error":"` + sensitiveBody + `"}`))
})) }))
defer ts.Close() defer ts.Close()
@@ -923,8 +924,11 @@ func TestOpenAICompatibleClientNon2xxError(t *testing.T) {
if !errors.Is(err, ErrUnexpectedStatus) { if !errors.Is(err, ErrUnexpectedStatus) {
t.Fatalf("expected ErrUnexpectedStatus, got %v", err) t.Fatalf("expected ErrUnexpectedStatus, got %v", err)
} }
if !strings.Contains(err.Error(), "400") || !strings.Contains(err.Error(), "bad request payload") { if !strings.Contains(err.Error(), "status=400") {
t.Fatalf("expected status/body details, got %v", err) t.Fatalf("expected status detail, got %v", err)
}
if strings.Contains(err.Error(), sensitiveBody) {
t.Fatalf("expected provider response body to be redacted, got %v", err)
} }
} }