> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchport.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling Examples

> Comprehensive error handling patterns for production applications

## Overview

This guide covers robust error handling patterns for production use:

* Catching specific errors
* Implementing fallback behavior
* Retry logic with exponential backoff
* Logging and monitoring
* Graceful degradation

## Basic Error Handling

Handle the most common errors:

```go theme={null}
package main

import (
	"errors"
	"fmt"
	"log"

	"github.com/switchport-ai/switchport-go/switchport"
)

func main() {
	client, err := switchport.NewClient("")
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	response, err := client.Prompts.Execute("my-prompt", nil, nil)
	if err != nil {
		var authErr *switchport.AuthenticationError
		var promptNotFound *switchport.PromptNotFoundError
		var apiErr *switchport.APIError

		switch {
		case errors.As(err, &authErr):
			fmt.Println("Authentication failed. Check your API key.")
		case errors.As(err, &promptNotFound):
			fmt.Println("Prompt not found. Create it in the dashboard.")
		case errors.As(err, &apiErr):
			fmt.Printf("API error: %v\n", apiErr)
			fmt.Printf("Status code: %d\n", apiErr.StatusCode)
		default:
			fmt.Printf("Unexpected error: %v\n", err)
		}
		return
	}

	fmt.Println(response.Text)
}
```

## Fallback Behavior

Provide default responses when the API fails:

```go theme={null}
package main

import (
	"fmt"
	"log"

	"github.com/switchport-ai/switchport-go/switchport"
)

func getWelcomeMessage(client *switchport.Client, userName string) string {
	// Try to get AI-generated message
	response, err := client.Prompts.Execute(
		"welcome-message",
		nil,
		map[string]interface{}{"name": userName},
	)

	if err != nil {
		var switchportErr *switchport.SwitchportError
		if errors.As(err, &switchportErr) {
			log.Printf("Switchport error: %v", err)
		}

		// Fallback to default message
		return fmt.Sprintf("Welcome, %s! Thanks for joining us.", userName)
	}

	return response.Text
}

func main() {
	client, err := switchport.NewClient("")
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	message := getWelcomeMessage(client, "Alice")
	fmt.Println(message)
}
```

## Retry Logic with Exponential Backoff

Implement retry logic for transient failures:

```go theme={null}
package main

import (
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/switchport-ai/switchport-go/switchport"
)

func executeWithRetry(
	client *switchport.Client,
	promptKey string,
	subject switchport.Subject,
	variables map[string]interface{},
	maxRetries int,
	initialDelay time.Duration,
) (*switchport.PromptResponse, error) {
	delay := initialDelay

	for attempt := 0; attempt < maxRetries; attempt++ {
		response, err := client.Prompts.Execute(promptKey, subject, variables)

		if err == nil {
			return response, nil
		}

		// Check if we should retry
		var apiErr *switchport.APIError
		shouldRetry := false

		if errors.As(err, &apiErr) {
			// Retry on 5xx errors or rate limiting (429)
			if apiErr.StatusCode >= 500 || apiErr.StatusCode == 429 {
				shouldRetry = true
			}
		}

		if shouldRetry && attempt < maxRetries-1 {
			log.Printf("Attempt %d failed. Retrying in %v...", attempt+1, delay)
			time.Sleep(delay)
			delay *= 2 // Exponential backoff
		} else {
			return nil, err
		}
	}

	return nil, fmt.Errorf("max retries (%d) reached", maxRetries)
}

func main() {
	client, err := switchport.NewClient("")
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	response, err := executeWithRetry(
		client,
		"my-prompt",
		nil,
		map[string]interface{}{"name": "Alice"},
		3,                  // max retries
		1*time.Second,      // initial delay
	)

	if err != nil {
		log.Printf("Failed after retries: %v", err)
		return
	}

	fmt.Println(response.Text)
}
```

## Logging for Production

Comprehensive logging for debugging and monitoring:

```go theme={null}
package main

import (
	"log"

	"github.com/switchport-ai/switchport-go/switchport"
)

type PromptExecutor struct {
	client *switchport.Client
	logger *log.Logger
}

func NewPromptExecutor(client *switchport.Client) *PromptExecutor {
	return &PromptExecutor{
		client: client,
		logger: log.Default(),
	}
}

func (e *PromptExecutor) ExecuteWithLogging(
	promptKey string,
	subject switchport.Subject,
	variables map[string]interface{},
) (*switchport.PromptResponse, error) {
	e.logger.Printf("Executing prompt: key=%s, subject=%v, variables=%v",
		promptKey, subject, variables)

	response, err := e.client.Prompts.Execute(promptKey, subject, variables)

	if err != nil {
		e.logger.Printf("Prompt execution failed: key=%s, error=%v",
			promptKey, err)
		return nil, err
	}

	e.logger.Printf("Prompt executed successfully: key=%s, version=%s, request_id=%s",
		promptKey, response.VersionName, response.RequestID)

	return response, nil
}

func (e *PromptExecutor) RecordMetricWithLogging(
	metricKey string,
	value interface{},
	subject switchport.Subject,
) error {
	e.logger.Printf("Recording metric: key=%s, value=%v, subject=%v",
		metricKey, value, subject)

	result, err := e.client.Metrics.Record(metricKey, value, subject, nil)

	if err != nil {
		e.logger.Printf("Metric recording failed: key=%s, error=%v",
			metricKey, err)
		return err
	}

	e.logger.Printf("Metric recorded successfully: key=%s, event_id=%s",
		metricKey, result.MetricEventID)

	return nil
}

func main() {
	client, err := switchport.NewClient("")
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	executor := NewPromptExecutor(client)

	response, err := executor.ExecuteWithLogging(
		"welcome-message",
		map[string]interface{}{"user_id": "user_123"},
		map[string]interface{}{"name": "Alice"},
	)

	if err != nil {
		log.Printf("Error: %v", err)
		return
	}

	log.Printf("Response: %s", response.Text)
}
```

## Circuit Breaker Pattern

Prevent cascading failures:

```go theme={null}
package main

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/switchport-ai/switchport-go/switchport"
)

type CircuitState int

const (
	StateClosed CircuitState = iota
	StateOpen
	StateHalfOpen
)

type CircuitBreaker struct {
	client         *switchport.Client
	maxFailures    int
	resetTimeout   time.Duration
	failureCount   int
	lastFailTime   time.Time
	state          CircuitState
	mu             sync.RWMutex
}

func NewCircuitBreaker(client *switchport.Client, maxFailures int, resetTimeout time.Duration) *CircuitBreaker {
	return &CircuitBreaker{
		client:       client,
		maxFailures:  maxFailures,
		resetTimeout: resetTimeout,
		state:        StateClosed,
	}
}

func (cb *CircuitBreaker) Execute(
	promptKey string,
	subject switchport.Subject,
	variables map[string]interface{},
) (*switchport.PromptResponse, error) {
	cb.mu.Lock()

	// Check if circuit should reset
	if cb.state == StateOpen && time.Since(cb.lastFailTime) > cb.resetTimeout {
		cb.state = StateHalfOpen
		cb.failureCount = 0
	}

	// Circuit is open - fail fast
	if cb.state == StateOpen {
		cb.mu.Unlock()
		return nil, errors.New("circuit breaker is open")
	}

	cb.mu.Unlock()

	// Attempt execution
	response, err := cb.client.Prompts.Execute(promptKey, subject, variables)

	cb.mu.Lock()
	defer cb.mu.Unlock()

	if err != nil {
		cb.failureCount++
		cb.lastFailTime = time.Now()

		if cb.failureCount >= cb.maxFailures {
			cb.state = StateOpen
			fmt.Printf("Circuit breaker opened after %d failures\n", cb.failureCount)
		}

		return nil, err
	}

	// Success - reset circuit
	if cb.state == StateHalfOpen {
		cb.state = StateClosed
		cb.failureCount = 0
		fmt.Println("Circuit breaker closed - service recovered")
	}

	return response, nil
}

func main() {
	client, err := switchport.NewClient("")
	if err != nil {
		fmt.Printf("Failed to create client: %v\n", err)
		return
	}

	cb := NewCircuitBreaker(client, 3, 30*time.Second)

	// Simulate multiple requests
	for i := 0; i < 10; i++ {
		response, err := cb.Execute(
			"my-prompt",
			map[string]interface{}{"request_num": i},
			nil,
		)

		if err != nil {
			fmt.Printf("Request %d failed: %v\n", i, err)
			time.Sleep(1 * time.Second)
			continue
		}

		fmt.Printf("Request %d succeeded: %s\n", i, response.VersionName)
	}
}
```

## Graceful Degradation

Handle API unavailability gracefully:

```go theme={null}
package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/switchport-ai/switchport-go/switchport"
)

type MessageService struct {
	client        *switchport.Client
	cache         map[string]string
	fallbackMode  bool
}

func NewMessageService(client *switchport.Client) *MessageService {
	return &MessageService{
		client:       client,
		cache:        make(map[string]string),
		fallbackMode: false,
	}
}

func (s *MessageService) GetMessage(userID, userName string) string {
	// Try cache first
	if cached, ok := s.cache[userID]; ok {
		return cached
	}

	// Try API with timeout
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	responseChan := make(chan string, 1)
	errorChan := make(chan error, 1)

	go func() {
		response, err := s.client.Prompts.Execute(
			"welcome-message",
			map[string]interface{}{"user_id": userID},
			map[string]interface{}{"name": userName},
		)
		if err != nil {
			errorChan <- err
			return
		}
		responseChan <- response.Text
	}()

	select {
	case <-ctx.Done():
		// Timeout - use fallback
		s.fallbackMode = true
		return s.getFallbackMessage(userName)
	case err := <-errorChan:
		var apiErr *switchport.APIError
		if errors.As(err, &apiErr) && apiErr.StatusCode >= 500 {
			s.fallbackMode = true
		}
		return s.getFallbackMessage(userName)
	case message := <-responseChan:
		// Success - cache and return
		s.cache[userID] = message
		s.fallbackMode = false
		return message
	}
}

func (s *MessageService) getFallbackMessage(userName string) string {
	return fmt.Sprintf("Welcome, %s! We're glad to have you here.", userName)
}

func (s *MessageService) IsInFallbackMode() bool {
	return s.fallbackMode
}

func main() {
	client, err := switchport.NewClient("")
	if err != nil {
		fmt.Printf("Failed to create client: %v\n", err)
		return
	}

	service := NewMessageService(client)

	// Get messages for multiple users
	users := map[string]string{
		"user_001": "Alice",
		"user_002": "Bob",
		"user_003": "Carol",
	}

	for userID, userName := range users {
		message := service.GetMessage(userID, userName)
		fmt.Printf("%s: %s\n", userName, message)
	}

	if service.IsInFallbackMode() {
		fmt.Println("\n⚠️  Service is in fallback mode")
	}
}
```

## Error Reporting and Monitoring

Integration with error tracking services:

```go theme={null}
package main

import (
	"errors"
	"fmt"
	"log"

	"github.com/switchport-ai/switchport-go/switchport"
)

type ErrorReporter interface {
	Report(err error, context map[string]interface{})
}

type ConsoleReporter struct{}

func (r *ConsoleReporter) Report(err error, context map[string]interface{}) {
	log.Printf("Error reported: %v, Context: %v", err, context)
}

type SwitchportClient struct {
	client   *switchport.Client
	reporter ErrorReporter
}

func NewSwitchportClient(client *switchport.Client, reporter ErrorReporter) *SwitchportClient {
	return &SwitchportClient{
		client:   client,
		reporter: reporter,
	}
}

func (sc *SwitchportClient) ExecutePrompt(
	promptKey string,
	subject switchport.Subject,
	variables map[string]interface{},
) (*switchport.PromptResponse, error) {
	response, err := sc.client.Prompts.Execute(promptKey, subject, variables)

	if err != nil {
		// Report error with user identification
		errorContext := map[string]interface{}{
			"operation":  "execute_prompt",
			"prompt_key": promptKey,
			"context":    context,
		}

		var apiErr *switchport.APIError
		if errors.As(err, &apiErr) {
			errorContext["status_code"] = apiErr.StatusCode
			errorContext["response_data"] = apiErr.ResponseData
		}

		sc.reporter.Report(err, errorContext)
		return nil, err
	}

	return response, nil
}

func main() {
	client, err := switchport.NewClient("")
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	reporter := &ConsoleReporter{}
	scClient := NewSwitchportClient(client, reporter)

	response, err := scClient.ExecutePrompt(
		"my-prompt",
		map[string]interface{}{"user_id": "user_123"},
		nil,
	)

	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Println(response.Text)
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always implement fallbacks">
    Never let API failures completely break your application. Always have a fallback strategy.
  </Accordion>

  <Accordion title="Use timeouts">
    Set reasonable timeouts for API calls to prevent hanging requests.
  </Accordion>

  <Accordion title="Log with user identification">
    Include request IDs, user IDs, and other context in your logs for easier debugging.
  </Accordion>

  <Accordion title="Monitor error rates">
    Track error rates and set up alerts for unusual spikes.
  </Accordion>

  <Accordion title="Retry intelligently">
    Only retry on transient errors (5xx, network issues). Don't retry on 4xx errors.
  </Accordion>

  <Accordion title="Use circuit breakers">
    Implement circuit breakers to prevent cascading failures in high-traffic applications.
  </Accordion>

  <Accordion title="Test error paths">
    Regularly test your error handling code to ensure fallbacks work as expected.
  </Accordion>
</AccordionGroup>

## Next Steps

<Columns cols={2}>
  <Card title="API Reference" icon="code" href="/sdk/go/reference/errors">
    See all error types and their properties
  </Card>

  <Card title="Basic Examples" icon="book" href="/sdk/go/examples/basic-usage">
    Learn basic SDK usage patterns
  </Card>
</Columns>
