Skip to main content

Overview

The Auth0 Go SDK provides typed error handling for both the Authentication API and Management API, allowing you to programmatically detect and handle specific error conditions.

Authentication API Errors

The Authentication API uses the authentication.Error type, which includes the HTTP status code, error code, and descriptive message.

Error Structure

authentication/authentication_error.go
type Error struct {
    StatusCode int    `json:"statusCode"`
    Err        string `json:"error"`
    Message    string `json:"error_description"`
    MFAToken   string `json:"mfa_token,omitempty"`
}

Basic Error Handling

import (
    "github.com/auth0/go-auth0/v2/authentication"
    "github.com/auth0/go-auth0/v2/authentication/oauth"
)

tokens, err := auth.OAuth.LoginWithPassword(
    context.Background(),
    oauth.LoginWithPasswordRequest{
        Username: "[email protected]",
        Password: "hunter2",
    },
    oauth.IDTokenValidationOptions{},
)

if err != nil {
    // Check if it's an Authentication API error
    if aerr, ok := err.(*authentication.Error); ok {
        fmt.Printf("Auth error: %d %s: %s\n",
            aerr.StatusCode,
            aerr.Err,
            aerr.Message,
        )
    }
    return err
}

Handling Specific Error Codes

You can check the Err field to handle specific error conditions:
tokens, err := auth.OAuth.LoginWithPassword(
    context.Background(),
    oauth.LoginWithPasswordRequest{
        Username: "[email protected]",
        Password: "password",
    },
    oauth.IDTokenValidationOptions{},
)

if err != nil {
    if aerr, ok := err.(*authentication.Error); ok {
        switch aerr.Err {
        case "mfa_required":
            // Handle MFA requirement
            mfaToken := aerr.GetMFAToken()
            fmt.Printf("MFA required. Token: %s\n", mfaToken)
            // Prompt user for MFA code
            
        case "invalid_grant":
            // Handle invalid credentials
            fmt.Println("Invalid username or password")
            
        case "access_denied":
            // Handle access denied
            fmt.Println("Access denied")
            
        default:
            fmt.Printf("Authentication error: %s\n", aerr.Message)
        }
    }
    return err
}

Error Methods

The authentication.Error type provides several helper methods:
authentication/authentication_error.go
// Error formats the error into a string representation
func (a *Error) Error() string {
    return fmt.Sprintf("%d %s: %s", a.StatusCode, a.Err, a.Message)
}

// GetMFAToken returns the MFA token associated with the error, if any
func (a *Error) GetMFAToken() string {
    if a == nil || a.MFAToken == "" {
        return ""
    }
    return a.MFAToken
}

// Status returns the status code of the error
func (a *Error) Status() int {
    return a.StatusCode
}

Management API Errors

The Management API provides typed errors for different HTTP status codes. All errors embed the core.APIError type.

Base Error Type

management/core/api_error.go
type APIError struct {
    err error
    
    StatusCode int         `json:"-"`
    Header     http.Header `json:"-"`
}

Typed Error Types

The Management API defines specific error types for each HTTP status code:
management/errors.go
// 400 Bad Request
type BadRequestError struct {
    *core.APIError
    Body interface{}
}

// 401 Unauthorized
type UnauthorizedError struct {
    *core.APIError
    Body interface{}
}

// 403 Forbidden
type ForbiddenError struct {
    *core.APIError
    Body interface{}
}

// 404 Not Found
type NotFoundError struct {
    *core.APIError
    Body interface{}
}

// 409 Conflict
type ConflictError struct {
    *core.APIError
    Body interface{}
}

// 429 Too Many Requests
type TooManyRequestsError struct {
    *core.APIError
    Body interface{}
}

// 500 Internal Server Error
type InternalServerError struct {
    *core.APIError
    Body interface{}
}

Handling Management API Errors

import (
    "errors"
    "github.com/auth0/go-auth0/v2/management"
)

client, err := mgmt.Clients.Get(ctx, clientID)
if err != nil {
    // Check for specific error types
    var notFoundErr *management.NotFoundError
    if errors.As(err, &notFoundErr) {
        fmt.Println("Client not found")
        return nil // Handle gracefully
    }
    
    var forbiddenErr *management.ForbiddenError
    if errors.As(err, &forbiddenErr) {
        fmt.Printf("Insufficient permissions: %d\n", forbiddenErr.StatusCode)
        return err
    }
    
    var rateLimitErr *management.TooManyRequestsError
    if errors.As(err, &rateLimitErr) {
        // Check rate limit headers
        retryAfter := rateLimitErr.Header.Get("X-RateLimit-Reset")
        fmt.Printf("Rate limited. Retry after: %s\n", retryAfter)
        return err
    }
    
    return err
}

Common Error Scenarios

Handling Not Found Errors

user, err := mgmt.Users.Get(ctx, userID)
if err != nil {
    var notFoundErr *management.NotFoundError
    if errors.As(err, &notFoundErr) {
        // User doesn't exist, create a new one
        return createNewUser(ctx, mgmt)
    }
    return err
}

Handling Rate Limiting

import "time"

func listClientsWithRetry(ctx context.Context, mgmt *client.Management) error {
    clients, err := mgmt.Clients.List(ctx, &management.ListClientsRequestParameters{})
    if err != nil {
        var rateLimitErr *management.TooManyRequestsError
        if errors.As(err, &rateLimitErr) {
            // Wait and retry
            time.Sleep(1 * time.Second)
            return listClientsWithRetry(ctx, mgmt)
        }
        return err
    }
    // Process clients
    return nil
}

Handling Permission Errors

_, err := mgmt.Users.Delete(ctx, userID)
if err != nil {
    var forbiddenErr *management.ForbiddenError
    if errors.As(err, &forbiddenErr) {
        fmt.Println("Error: Insufficient scope to delete users")
        fmt.Println("Required scope: delete:users")
        return fmt.Errorf("permission denied: %w", err)
    }
    return err
}

Handling Conflict Errors

createRequest := &management.CreateClientRequestContent{
    Name:    "My Application",
    AppType: &management.ClientAppTypeEnumSpa,
}

client, err := mgmt.Clients.Create(ctx, createRequest)
if err != nil {
    var conflictErr *management.ConflictError
    if errors.As(err, &conflictErr) {
        fmt.Println("Client with this name already exists")
        // Try with a different name or update existing
        return nil
    }
    return err
}

Error Unwrapping

Both Authentication and Management API errors support Go’s error unwrapping:
import "errors"

// Check if an error is or wraps a specific type
if errors.Is(err, someSpecificError) {
    // Handle specific error
}

// Extract the underlying error
var apiErr *core.APIError
if errors.As(err, &apiErr) {
    fmt.Printf("Status code: %d\n", apiErr.StatusCode)
}

Best Practices

Use type assertions or errors.As to check for specific error types before falling back to generic error handling. This allows you to provide better user feedback and handle recoverable errors appropriately.
Check for TooManyRequestsError and implement exponential backoff or respect the X-RateLimit-Reset header to avoid overwhelming the API.
When logging errors, include the status code, error message, and any relevant headers. This helps with troubleshooting production issues.
When working with error fields that may be nil, use the provided getter methods (e.g., GetMFAToken()) to avoid nil pointer panics.

Next Steps

Authentication API

Learn about the Authentication API client

Management API

Learn about the Management API client

Build docs developers (and LLMs) love