@@ -68,12 +68,21 @@ func classifyConnectError(err *connect.Error) *Failure {
6868 case connect .CodeInvalidArgument , connect .CodeOutOfRange , connect .CodeFailedPrecondition :
6969 failure .StatusCode , failure .ClientFault = 400 , true
7070 case connect .CodeUnauthenticated :
71- failure .StatusCode , failure .CredentialFailed = 401 , true
71+ // 上游抖动会把内部错误包装成 unauthenticated/permission_denied
72+ // (如 "an internal error occurred"),此类按可重试上游错误处理,
73+ // 不能把账号标成凭证失效。
74+ if isUpstreamInternalError (message ) {
75+ failure .StatusCode , failure .Retryable = 502 , true
76+ } else {
77+ failure .StatusCode , failure .CredentialFailed = 401 , true
78+ }
7279 case connect .CodePermissionDenied :
7380 // 上游对「内容策略拦截」和「凭证无权限」都回 permission_denied。
7481 // 前者是请求级拒绝,按客户端错误处理;不能把账号标成凭证失效。
7582 if isContentPolicyRejection (message ) {
7683 failure .StatusCode , failure .ClientFault = 400 , true
84+ } else if isUpstreamInternalError (message ) {
85+ failure .StatusCode , failure .Retryable = 502 , true
7786 } else {
7887 failure .StatusCode , failure .CredentialFailed = 403 , true
7988 }
@@ -150,3 +159,10 @@ func isContentPolicyRejection(message string) bool {
150159 lower := strings .ToLower (message )
151160 return strings .Contains (lower , "content policy" ) || strings .Contains (lower , "content_policy" )
152161}
162+
163+ // isUpstreamInternalError 识别上游内部错误被包装成 auth 类 code 的措辞,
164+ // 例如 "an internal error occurred (trace ID: ...)"。
165+ func isUpstreamInternalError (message string ) bool {
166+ lower := strings .ToLower (message )
167+ return strings .Contains (lower , "internal error" ) || strings .Contains (lower , "internal_error" )
168+ }
0 commit comments