fix(providers): surface friendly auth error messages (#3198)

This commit is contained in:
LC
2026-06-30 17:42:07 +08:00
committed by GitHub
parent 52320f4875
commit 2cf030d2fd
8 changed files with 482 additions and 15 deletions
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
@@ -21,7 +20,7 @@ func (al *AgentLoop) maybePublishError(ctx context.Context, channel, chatID, ses
if errors.Is(err, context.Canceled) {
return false
}
al.PublishResponseIfNeeded(ctx, channel, chatID, sessionKey, fmt.Sprintf("Error processing message: %v", err))
al.PublishResponseIfNeeded(ctx, channel, chatID, sessionKey, formatProcessingError(err))
return true
}
+36
View File
@@ -0,0 +1,36 @@
package agent
import (
"fmt"
"github.com/sipeed/picoclaw/pkg/providers"
)
func formatProcessingError(err error) string {
if err == nil {
return ""
}
if kind, ok := providers.ClassifyAuthError(err); ok {
return fmt.Sprintf(
"Error processing message: %s\n\nOriginal error:\n%s",
authErrorFriendlyMessage(kind),
err.Error(),
)
}
return fmt.Sprintf("Error processing message: %v", err)
}
func authErrorFriendlyMessage(kind providers.AuthErrorKind) string {
switch kind {
case providers.AuthErrorInvalidAPIKey:
return "Authentication failed: the API key appears to be invalid. Check the API key configured for this model or provider."
case providers.AuthErrorMissingAPIKey:
return "Authentication failed: no API key is configured for this model or provider. Add an API key in the model settings or config."
case providers.AuthErrorExpiredToken:
return "Authentication failed: the saved login or token appears to be expired. Re-authenticate the provider."
default:
return "Authentication failed: check the API key, token, OAuth login, or provider permissions for this model."
}
}
+52
View File
@@ -0,0 +1,52 @@
package agent
import (
"errors"
"strings"
"testing"
"github.com/sipeed/picoclaw/pkg/providers/common"
)
func TestFormatProcessingError_InvalidAPIKey(t *testing.T) {
err := errors.New(
`LLM call failed after retries: API request failed: Status: 401 Body: {"error":{"message":"Incorrect API key provided"}}`,
)
got := formatProcessingError(err)
if !strings.Contains(got, "API key appears to be invalid") {
t.Fatalf("formatted error missing friendly API key hint: %q", got)
}
if !strings.Contains(got, "Original error:") {
t.Fatalf("formatted error missing original error label: %q", got)
}
if !strings.Contains(got, err.Error()) {
t.Fatalf("formatted error missing original error: %q", got)
}
}
func TestFormatProcessingError_GenericAuthHTTPError(t *testing.T) {
err := &common.HTTPError{
StatusCode: 401,
BodyPreview: `{"error":"unauthorized"}`,
ContentType: "application/json",
APIBase: "https://api.example.com",
}
got := formatProcessingError(err)
if !strings.Contains(got, "check the API key, token, OAuth login, or provider permissions") {
t.Fatalf("formatted error missing generic auth hint: %q", got)
}
if !strings.Contains(got, "Original error:") {
t.Fatalf("formatted error missing original error: %q", got)
}
}
func TestFormatProcessingError_NonAuth(t *testing.T) {
err := errors.New("connection reset by peer")
got := formatProcessingError(err)
want := "Error processing message: connection reset by peer"
if got != want {
t.Fatalf("formatted error = %q, want %q", got, want)
}
}