mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-07-28 01:27:58 +00:00
fix(providers): surface friendly auth error messages (#3198)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user