LLM Error Handling & Retry: Best Practices Design
Scope: Structured error classification, automatic retry mechanisms, fallback strategies, and recovery patterns for LLM client libraries and agent frameworks.
Synthesized from: pydantic-ai, langchain, pi-mono, kosong, republic
Core Philosophy
Errors are data. Recovery is strategy. Decisions are context-dependent.
This design philosophy combines:
- Type safety (compile-time) for precise error handling
- Strategy flexibility (runtime) for adaptable recovery
- Observability for production debugging
- Testability for chaos engineering
1. Dual-Layer Error System
1.1 Type Layer: Precise Error Types
/// Hierarchical error types for match-based handling
pub enum LLMError {
/// Developer misuse (bad API key, invalid model name)
User {
kind: UserErrorKind,
message: String,
},
/// Runtime errors during LLM interaction
Runtime(RuntimeError),
/// Wrapped error with recovery strategy attached
Retryable {
source: Box<LLMError>,
strategy: RetryStrategy,
},
}
pub enum RuntimeError {
Connection {
endpoint: String,
source: Option<Box<dyn std::error::Error>>,
},
Status {
code: u16,
body: Option<String>,
provider: ProviderId,
},
Validation {
field: String,
reason: String,
},
TokenLimit {
requested: usize,
max_tokens: Option<usize>,
},
ContentFilter {
provider: ProviderId,
reason: Option<String>,
},
ToolCallIncomplete {
partial: ToolCall,
},
}
pub enum UserErrorKind {
InvalidApiKey,
ModelNotFound,
InvalidParameter,
UnsupportedFeature,
}
Design Rationale:
- Explicit types enable exhaustive match handling
- Rich context (status codes, provider IDs) aids debugging
- Separate user errors from runtime errors for different handling paths