@@ -6,6 +6,7 @@ package handlers
66import (
77 "bytes"
88 "encoding/json"
9+ "errors"
910 "fmt"
1011 "net/http"
1112 "strings"
@@ -492,6 +493,7 @@ func (h *BaseAPIHandler) ExecuteWithAuthManager(ctx context.Context, handlerType
492493 opts .Metadata = reqMeta
493494 resp , err := h .AuthManager .Execute (ctx , providers , req , opts )
494495 if err != nil {
496+ err = enrichAuthSelectionError (err , providers , normalizedModel )
495497 status := http .StatusInternalServerError
496498 if se , ok := err .(interface { StatusCode () int }); ok && se != nil {
497499 if code := se .StatusCode (); code > 0 {
@@ -538,6 +540,7 @@ func (h *BaseAPIHandler) ExecuteCountWithAuthManager(ctx context.Context, handle
538540 opts .Metadata = reqMeta
539541 resp , err := h .AuthManager .ExecuteCount (ctx , providers , req , opts )
540542 if err != nil {
543+ err = enrichAuthSelectionError (err , providers , normalizedModel )
541544 status := http .StatusInternalServerError
542545 if se , ok := err .(interface { StatusCode () int }); ok && se != nil {
543546 if code := se .StatusCode (); code > 0 {
@@ -588,6 +591,7 @@ func (h *BaseAPIHandler) ExecuteStreamWithAuthManager(ctx context.Context, handl
588591 opts .Metadata = reqMeta
589592 streamResult , err := h .AuthManager .ExecuteStream (ctx , providers , req , opts )
590593 if err != nil {
594+ err = enrichAuthSelectionError (err , providers , normalizedModel )
591595 errChan := make (chan * interfaces.ErrorMessage , 1 )
592596 status := http .StatusInternalServerError
593597 if se , ok := err .(interface { StatusCode () int }); ok && se != nil {
@@ -697,7 +701,7 @@ func (h *BaseAPIHandler) ExecuteStreamWithAuthManager(ctx context.Context, handl
697701 chunks = retryResult .Chunks
698702 continue outer
699703 }
700- streamErr = retryErr
704+ streamErr = enrichAuthSelectionError ( retryErr , providers , normalizedModel )
701705 }
702706 }
703707
@@ -840,6 +844,54 @@ func replaceHeader(dst http.Header, src http.Header) {
840844 }
841845}
842846
847+ func enrichAuthSelectionError (err error , providers []string , model string ) error {
848+ if err == nil {
849+ return nil
850+ }
851+
852+ var authErr * coreauth.Error
853+ if ! errors .As (err , & authErr ) || authErr == nil {
854+ return err
855+ }
856+
857+ code := strings .TrimSpace (authErr .Code )
858+ if code != "auth_not_found" && code != "auth_unavailable" {
859+ return err
860+ }
861+
862+ providerText := strings .Join (providers , "," )
863+ if providerText == "" {
864+ providerText = "unknown"
865+ }
866+ modelText := strings .TrimSpace (model )
867+ if modelText == "" {
868+ modelText = "unknown"
869+ }
870+
871+ baseMessage := strings .TrimSpace (authErr .Message )
872+ if baseMessage == "" {
873+ baseMessage = "no auth available"
874+ }
875+ detail := fmt .Sprintf ("%s (providers=%s, model=%s)" , baseMessage , providerText , modelText )
876+
877+ // Clarify the most common alias confusion between Anthropic route names and internal provider keys.
878+ if strings .Contains ("," + providerText + "," , ",claude," ) {
879+ detail += "; check Claude auth/key session and cooldown state via /v0/management/auth-files"
880+ }
881+
882+ status := authErr .HTTPStatus
883+ if status <= 0 {
884+ status = http .StatusServiceUnavailable
885+ }
886+
887+ return & coreauth.Error {
888+ Code : authErr .Code ,
889+ Message : detail ,
890+ Retryable : authErr .Retryable ,
891+ HTTPStatus : status ,
892+ }
893+ }
894+
843895// WriteErrorResponse writes an error message to the response writer using the HTTP status embedded in the message.
844896func (h * BaseAPIHandler ) WriteErrorResponse (c * gin.Context , msg * interfaces.ErrorMessage ) {
845897 status := http .StatusInternalServerError
0 commit comments