error-handling-guide.mdβ’7.58 kB
# 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:
```swift
// 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:
```swift
// 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:
```swift
// 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:
```swift
// 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:
```swift
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:
```swift
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
```swift
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:
```bash
# 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-output
{
"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:
```swift
// β
Good
throw NotFoundError.application("TextEdit")
// β Avoid
throw NSError(domain: "PeekabooError", code: 404, userInfo: nil)
```
### 2. Provide Context
Include relevant context in errors:
```swift
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:
```swift
// 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:
```swift
// 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:
```swift
// Before
throw NSError(domain: "Peekaboo", code: 1, userInfo: [
NSLocalizedDescriptionKey: "App not found"
])
// After
throw NotFoundError.application(appName)
```
## Testing Errors
Test error handling comprehensively:
```swift
@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