Strip the API key from fixed error text and keep typed matches

ProviderError and the new NewRateLimitedError constructor now keep the
credential and remove it from their complete Error() text, including
fixed words such as "HTTP" and "rate", so errors.As to them succeeds
through the adapter's redaction stand-in instead of being refused. The
openai adapter builds RateLimitedError with the key. Tests cover the keys
"HTTP" (500), "rate" (429) and "openai" (401 and no model), checking
Error(), Body(), every unwrap layer, errors.Is and errors.As.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 07:36:56 -04:00
co-authored by Claude Opus 5.5
parent 68391d14c8
commit 893e00d14e
4 changed files with 126 additions and 9 deletions
+5 -3
View File
@@ -446,7 +446,7 @@ func (p *Provider) statusError(resp *http.Response, body []byte) error {
case http.StatusUnauthorized, http.StatusForbidden:
return fmt.Errorf("HTTP %d: %w", resp.StatusCode, llm.ErrAuth)
case http.StatusTooManyRequests:
return &llm.RateLimitedError{RetryAfter: retryAfter(resp.Header.Get("Retry-After"), time.Now())}
return llm.NewRateLimitedError(retryAfter(resp.Header.Get("Retry-After"), time.Now()), p.apiKey)
case http.StatusRequestTimeout:
return fmt.Errorf("HTTP %d: %w", resp.StatusCode, llm.ErrTimeout)
default:
@@ -518,8 +518,10 @@ func (p *Provider) transportError(err error) error {
// API key. Its text is the layer's text with the key redacted, and Unwrap
// returns the layer's causes passed through redact, so no error reached by
// unwrapping has text holding the key. Is consults the original layer and
// its chain, so sentinels still match. As does too, but refuses a match
// whose text holds the key, so it never hands back an unredacted error.
// its chain, so sentinels still match. As does too. The llm typed errors
// remove the key from their own complete text, fixed words included, so
// they always match; As refuses only a match from outside llm whose text
// holds the key, so it never hands back an unredacted error.
type redactedError struct {
text string
err error