package authimport ( "encoding/json" "errors" "fmt" "strings")// ErrorCodeRequestScoped identifies failures tied to the current request rather// than the selected credential.const ErrorCodeRequestScoped = "request_scoped"const requestScopedErrorCode = ErrorCodeRequestScoped// ErrorCodeConnectionLifecycle marks transport/session lifecycle failures that// must skip credential cooldown without being treated as request-scoped faults.const ErrorCodeConnectionLifecycle = "connection_lifecycle"const connectionLifecycleErrorCode = ErrorCodeConnectionLifecycle// ErrorCodeTransientTransport marks pre-HTTP dial/TLS/DNS/reset failures that// must skip credential cooldown and remain eligible for request-retry rounds.const ErrorCodeTransientTransport = "transient_transport"const transientTransportErrorCode = ErrorCodeTransientTransport// ErrorCodeForceCooldown marks failures that must enforce credential cooldown.const ErrorCodeForceCooldown = "force_cooldown"// Error describes an authentication related failure in a provider agnostic format.type Error struct { // Code is a short machine readable identifier. Code string `json:"code,omitempty"` // Message is a human readable description of the failure. Message string `json:"message"` // Retryable indicates whether a retry might fix the issue automatically. Retryable bool `json:"retryable"` // HTTPStatus optionally records an HTTP-like status code for the error. HTTPStatus int `json:"http_status,omitempty"`}// IsTerminalAuthError checks if err or any error in its chain represents a permanent upstream auth failure.func IsTerminalAuthError(err error) bool { if err == nil { return false } type terminalAuthProvider interface { IsTerminalAuth() bool } var tap terminalAuthProvider if errors.As(err, &tap) && tap != nil { return tap.IsTerminalAuth() } return false}type terminalAuthError struct { *errorWithCause}func (e *terminalAuthError) IsTerminalAuth() bool { return true}// NewTerminalAuthError wraps an *Error and cause as a terminal upstream authentication failure.func NewTerminalAuthError(err *Error, cause error) error { if err == nil { return nil } base := WithCause(err, cause) if ewc, ok := base.(*errorWithCause); ok && ewc != nil { return &terminalAuthError{errorWithCause: ewc} } return &terminalAuthError{errorWithCause: &errorWithCause{base: err, cause: cause}}}// Error implements the error interface.func (e *Error) Error() string { if e == nil { return "" } if e.Code == "" { return e.Message } return e.Code + ": " + e.Message}// StatusCode implements optional status accessor for manager decision making.func (e *Error) StatusCode() int { if e == nil { return 0 } return e.HTTPStatus}// IsRequestScoped reports whether the failure is tied to the current request// rather than the selected credential.func (e *Error) IsRequestScoped() bool { return e != nil && e.Code == ErrorCodeRequestScoped}// MarkRequestScoped marks the error as request-scoped in place and returns it.func (e *Error) MarkRequestScoped() *Error { if e != nil { e.Code = ErrorCodeRequestScoped } return e}// NewRequestScopedError creates an Error explicitly flagged as request-scoped so// that credential cooldown is skipped.func NewRequestScopedError(message string, httpStatus int) *Error { return &Error{ Code: ErrorCodeRequestScoped, Message: message, HTTPStatus: httpStatus, }}type errorWithCause struct { base *Error cause error}func (e *errorWithCause) Error() string { if e == nil || e.base == nil { return "" } baseText := e.base.Error() if e.cause == nil { return baseText } summary := ExtractUpstreamErrorSummary(e.cause.Error()) if summary != "" && !strings.Contains(baseText, summary) { return fmt.Sprintf("%s (last upstream error: %s)", baseText, summary) } return baseText}func (e *errorWithCause) Unwrap() error { if e == nil { return nil } return e.cause}func (e *errorWithCause) As(target any) bool { if e == nil { return false } if t, ok := target.(**Error); ok { *t = e.base return true } return false}func (e *errorWithCause) Is(target error) bool { if e == nil { return target == nil } if target == e || target == e.base { return true } if other, ok := target.(*errorWithCause); ok && other != nil { return e.base == other.base } if otherErr, ok := target.(*Error); ok && otherErr != nil { return e.base == otherErr } return false}func (e *errorWithCause) StatusCode() int { if e == nil || e.base == nil { return 0 } return e.base.HTTPStatus}func (e *errorWithCause) IsRequestScoped() bool { if e == nil || e.base == nil { return false } return e.base.IsRequestScoped()}func (e *errorWithCause) MarkRequestScoped() *Error { if e == nil || e.base == nil { return nil } return e.base.MarkRequestScoped()}func (e *errorWithCause) MarshalJSON() ([]byte, error) { if e == nil || e.base == nil { return []byte("null"), nil } return json.Marshal(e.base)}// WithCause wraps an *Error with an underlying cause without changing the Error struct layout.func WithCause(err *Error, cause error) error { if err == nil { return nil } if cause == nil { return err } return &errorWithCause{ base: err, cause: cause, }}
package auth
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
// ErrorCodeRequestScoped identifies failures tied to the current request rather
// than the selected credential.
const ErrorCodeRequestScoped = "request_scoped"
const requestScopedErrorCode = ErrorCodeRequestScoped
// ErrorCodeConnectionLifecycle marks transport/session lifecycle failures that
// must skip credential cooldown without being treated as request-scoped faults.
const ErrorCodeConnectionLifecycle = "connection_lifecycle"
const connectionLifecycleErrorCode = ErrorCodeConnectionLifecycle
// ErrorCodeTransientTransport marks pre-HTTP dial/TLS/DNS/reset failures that
// must skip credential cooldown and remain eligible for request-retry rounds.
const ErrorCodeTransientTransport = "transient_transport"
const transientTransportErrorCode = ErrorCodeTransientTransport
// ErrorCodeForceCooldown marks failures that must enforce credential cooldown.
const ErrorCodeForceCooldown = "force_cooldown"
// Error describes an authentication related failure in a provider agnostic format.
type Error struct {
// Code is a short machine readable identifier.
Code string `json:"code,omitempty"`
// Message is a human readable description of the failure.
Message string `json:"message"`
// Retryable indicates whether a retry might fix the issue automatically.
Retryable bool `json:"retryable"`
// HTTPStatus optionally records an HTTP-like status code for the error.
HTTPStatus int `json:"http_status,omitempty"`
}
// IsTerminalAuthError checks if err or any error in its chain represents a permanent upstream auth failure.
func IsTerminalAuthError(err error) bool {
if err == nil {
return false
}
type terminalAuthProvider interface {
IsTerminalAuth() bool
}
var tap terminalAuthProvider
if errors.As(err, &tap) && tap != nil {
return tap.IsTerminalAuth()
}
return false
}
type terminalAuthError struct {
*errorWithCause
}
func (e *terminalAuthError) IsTerminalAuth() bool {
return true
}
// NewTerminalAuthError wraps an *Error and cause as a terminal upstream authentication failure.
func NewTerminalAuthError(err *Error, cause error) error {
if err == nil {
return nil
}
base := WithCause(err, cause)
if ewc, ok := base.(*errorWithCause); ok && ewc != nil {
return &terminalAuthError{errorWithCause: ewc}
}
return &terminalAuthError{errorWithCause: &errorWithCause{base: err, cause: cause}}
}
// Error implements the error interface.
func (e *Error) Error() string {
if e == nil {
return ""
}
if e.Code == "" {
return e.Message
}
return e.Code + ": " + e.Message
}
// StatusCode implements optional status accessor for manager decision making.
func (e *Error) StatusCode() int {
if e == nil {
return 0
}
return e.HTTPStatus
}
// IsRequestScoped reports whether the failure is tied to the current request
// rather than the selected credential.
func (e *Error) IsRequestScoped() bool {
return e != nil && e.Code == ErrorCodeRequestScoped
}
// MarkRequestScoped marks the error as request-scoped in place and returns it.
func (e *Error) MarkRequestScoped() *Error {
if e != nil {
e.Code = ErrorCodeRequestScoped
}
return e
}
// NewRequestScopedError creates an Error explicitly flagged as request-scoped so
// that credential cooldown is skipped.
func NewRequestScopedError(message string, httpStatus int) *Error {
return &Error{
Code: ErrorCodeRequestScoped,
Message: message,
HTTPStatus: httpStatus,
}
}
type errorWithCause struct {
base *Error
cause error
}
func (e *errorWithCause) Error() string {
if e == nil || e.base == nil {
return ""
}
baseText := e.base.Error()
if e.cause == nil {
return baseText
}
summary := ExtractUpstreamErrorSummary(e.cause.Error())
if summary != "" && !strings.Contains(baseText, summary) {
return fmt.Sprintf("%s (last upstream error: %s)", baseText, summary)
}
return baseText
}
func (e *errorWithCause) Unwrap() error {
if e == nil {
return nil
}
return e.cause
}
func (e *errorWithCause) As(target any) bool {
if e == nil {
return false
}
if t, ok := target.(**Error); ok {
*t = e.base
return true
}
return false
}
func (e *errorWithCause) Is(target error) bool {
if e == nil {
return target == nil
}
if target == e || target == e.base {
return true
}
if other, ok := target.(*errorWithCause); ok && other != nil {
return e.base == other.base
}
if otherErr, ok := target.(*Error); ok && otherErr != nil {
return e.base == otherErr
}
return false
}
func (e *errorWithCause) StatusCode() int {
if e == nil || e.base == nil {
return 0
}
return e.base.HTTPStatus
}
func (e *errorWithCause) IsRequestScoped() bool {
if e == nil || e.base == nil {
return false
}
return e.base.IsRequestScoped()
}
func (e *errorWithCause) MarkRequestScoped() *Error {
if e == nil || e.base == nil {
return nil
}
return e.base.MarkRequestScoped()
}
func (e *errorWithCause) MarshalJSON() ([]byte, error) {
if e == nil || e.base == nil {
return []byte("null"), nil
}
return json.Marshal(e.base)
}
// WithCause wraps an *Error with an underlying cause without changing the Error struct layout.
func WithCause(err *Error, cause error) error {
if err == nil {
return nil
}
if cause == nil {
return err
}
return &errorWithCause{
base: err,
cause: cause,
}
}