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: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: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: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: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: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: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: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
Always implement fallbacks
Always implement fallbacks
Never let API failures completely break your application. Always have a fallback strategy.
Use timeouts
Use timeouts
Set reasonable timeouts for API calls to prevent hanging requests.
Log with user identification
Log with user identification
Include request IDs, user IDs, and other context in your logs for easier debugging.
Monitor error rates
Monitor error rates
Track error rates and set up alerts for unusual spikes.
Retry intelligently
Retry intelligently
Only retry on transient errors (5xx, network issues). Don’t retry on 4xx errors.
Use circuit breakers
Use circuit breakers
Implement circuit breakers to prevent cascading failures in high-traffic applications.
Test error paths
Test error paths
Regularly test your error handling code to ensure fallbacks work as expected.
Next Steps
API Reference
See all error types and their properties
Basic Examples
Learn basic SDK usage patterns

