Peekaboo/docs/error-handling-guide.md
2026-03-13 03:47:30 +00:00

7.6 KiB

summary read_when
Review Peekaboo Error Handling Guide guidance
planning work related to peekaboo error handling guide
debugging or extending features described here

Peekaboo Error Handling Guide

This guide describes the unified error handling system in PeekabooCore, designed to provide consistent, user-friendly error messages across all services.

Overview

The error handling system consists of three main components:

  1. Standardized Errors - Consistent error types and codes
  2. Error Formatting - Unified presentation for CLI, JSON, and logs
  3. Error Recovery - Automatic retry and graceful degradation

Error Types

Standard Error Codes

All errors in Peekaboo use standardized error codes for consistency:

// Permission errors
case screenRecordingPermissionDenied = "PERMISSION_DENIED_SCREEN_RECORDING"
case accessibilityPermissionDenied = "PERMISSION_DENIED_ACCESSIBILITY"

// Not found errors
case applicationNotFound = "APP_NOT_FOUND"
case windowNotFound = "WINDOW_NOT_FOUND"
case elementNotFound = "ELEMENT_NOT_FOUND"

// Operation errors
case captureFailed = "CAPTURE_FAILED"
case interactionFailed = "INTERACTION_FAILED"
case timeout = "TIMEOUT"

Error Categories

Errors are grouped into categories:

  • Permission: Access control issues
  • Not Found: Missing resources
  • Operation: Execution failures
  • Validation: Input errors
  • System: Infrastructure issues
  • AI: AI provider problems

Using the Error System

Creating Errors

Use the predefined error types for consistency:

// Permission errors
throw PermissionError.screenRecording()
throw PermissionError.accessibility()

// Not found errors
throw NotFoundError.application("Safari")
throw NotFoundError.window(app: "Finder", index: 2)
throw NotFoundError.element("Submit button")

// Operation errors
throw OperationError.captureFailed(reason: "Display disconnected")
throw OperationError.timeout(operation: "screenshot", duration: 30)

// Validation errors
throw ValidationError.invalidInput(field: "coordinates", reason: "Outside screen bounds")
throw ValidationError.ambiguousAppIdentifier("Safari", matches: ["Safari", "Safari Technology Preview"])

Formatting Errors

Use ErrorFormatter for consistent presentation:

// For CLI output
let message = ErrorFormatter.formatForCLI(error, verbose: true)

// For JSON responses
let json = ErrorFormatter.formatForJSON(error)

// For logging
let logMessage = ErrorFormatter.formatForLog(error)

// For multiple errors
let summary = ErrorFormatter.formatMultipleErrors(errors)

Error Recovery

Retry Policies

Configure automatic retry behavior:

// Use standard retry policy (3 attempts, exponential backoff)
let result = try await RetryHandler.withRetry {
    try await captureScreen()
}

// Custom retry policy
let policy = RetryPolicy(
    maxAttempts: 5,
    initialDelay: 0.1,
    delayMultiplier: 2.0,
    retryableErrors: [.timeout, .captureFailed]
)

let result = try await RetryHandler.withRetry(policy: policy) {
    try await performOperation()
}

Recovery Suggestions

Errors include recovery suggestions:

let error = PermissionError.screenRecording()
if let suggestion = error.recoverySuggestion {
    print("Suggestion: \(suggestion)")
    // Output: "Grant Screen Recording permission in System Settings"
}

Graceful Degradation

Handle partial failures:

let options = DegradationOptions(
    allowPartialResults: true,
    fallbackToDefaults: true,
    skipNonCritical: true
)

// Operations can return degraded results
let result = DegradedResult(
    value: partialData,
    errors: [minorError],
    warnings: ["Some features unavailable"],
    isPartial: true
)

Service Integration

Example: ScreenCaptureService

public func captureScreen(displayIndex: Int? = nil) async throws -> CaptureResult {
    // Check permissions
    guard hasScreenRecordingPermission() else {
        throw PermissionError.screenRecording()
    }
    
    // Validate input
    if let index = displayIndex, index < 0 || index >= screenCount {
        throw ValidationError.invalidInput(
            field: "displayIndex",
            reason: "Must be between 0 and \(screenCount - 1)"
        )
    }
    
    // Perform capture with retry
    return try await RetryHandler.withRetry(policy: .standard) {
        guard let image = performCapture() else {
            throw OperationError.captureFailed(
                reason: "Unable to capture display"
            )
        }
        return CaptureResult(image: image)
    }
}

CLI Integration

Error Output

The CLI automatically formats errors based on output mode:

# Normal mode - user-friendly message
$ peekaboo capture
Error: Screen Recording permission is required. Please grant permission in System Settings > Privacy & Security > Screen Recording.

Suggestion: Grant Screen Recording permission in System Settings

# Verbose mode - includes context
$ peekaboo capture --verbose
Error: Screen Recording permission is required...

Suggestion: Grant Screen Recording permission in System Settings

Context:
  permission: screen_recording

# JSON mode - structured output
$ peekaboo capture --json
{
  "success": false,
  "error": {
    "error_code": "PERMISSION_DENIED_SCREEN_RECORDING",
    "message": "Screen Recording permission is required...",
    "recovery_suggestion": "Grant Screen Recording permission in System Settings",
    "context": {
      "permission": "screen_recording"
    }
  }
}

Best Practices

1. Use Standardized Errors

Always use the predefined error types instead of creating custom errors:

// ✅ Good
throw NotFoundError.application("TextEdit")

// ❌ Avoid
throw NSError(domain: "PeekabooError", code: 404, userInfo: nil)

2. Provide Context

Include relevant context in errors:

throw ValidationError.invalidCoordinates(x: 5000, y: 3000)
// Error includes the invalid coordinates in context

3. Use Appropriate Retry Policies

Choose retry policies based on operation type:

// Network operations - aggressive retry
RetryPolicy.aggressive

// User interactions - conservative retry
RetryPolicy.conservative

// Critical operations - no retry
RetryPolicy.noRetry

4. Handle Degraded Results

Design services to continue with partial data when appropriate:

// Allow partial window list if some windows fail
let windows = await collectWindows(options: .lenient)
if windows.isPartial {
    logger.warning("Some windows could not be accessed")
}

Migration Guide

To migrate existing error handling:

  1. Replace custom errors with standardized types
  2. Update error formatting to use ErrorFormatter
  3. Add retry logic where appropriate
  4. Implement recovery suggestions

Example migration:

// Before
throw NSError(domain: "Peekaboo", code: 1, userInfo: [
    NSLocalizedDescriptionKey: "App not found"
])

// After
throw NotFoundError.application(appName)

Testing Errors

Test error handling comprehensively:

@Test
func testPermissionError() async throws {
    let error = PermissionError.screenRecording()
    
    #expect(error.code == .screenRecordingPermissionDenied)
    #expect(error.userMessage.contains("Screen Recording"))
    #expect(error.recoverySuggestion \!= nil)
    
    let json = ErrorFormatter.formatForJSON(error)
    #expect(json["error_code"] as? String == "PERMISSION_DENIED_SCREEN_RECORDING")
}

Future Enhancements

Planned improvements:

  • Localization support for error messages
  • Error analytics and reporting
  • Advanced recovery strategies
  • Error aggregation for batch operations EOF < /dev/null