In this lesson, you will learn how to distinguish between errors that resolve themselves through repetition and those that indicate a terminal state for your application.
When building resilient systems, not all errors are created equal. We classify them into two buckets: Transient and Fatal.
Transient Errors
Transient errors are temporary failures—a network hiccup, a database timeout, or a locked file—that likely won’t exist a few milliseconds later. In Rust, these are your primary candidates for retry logic and exponential backoff. Because these errors are external to your application’s core logic, they do not invalidate the system state.
Fatal Errors
Fatal errors signify a violation of your application’s invariants, such as an invalid configuration file, a corrupted database schema, or an exhausted resource that cannot be recovered without human intervention or a hard restart. If you attempt to retry a fatal error, you risk wasting compute cycles or, worse, worsening the system's state.
Consider this categorical split in your error handling:
enum AppError {
NetworkTimeout, // Transient: Retryable
DatabaseUnavailable, // Transient: Retryable
InvalidConfig, // Fatal: Stop immediately
LogicCorruption, // Fatal: Panic or graceful shutdown
}
fn handle_error(err: AppError) {
match err {
AppError::NetworkTimeout | AppError::DatabaseUnavailable => {
// Trigger Exponential Backoff
retry_operation();
}
AppError::InvalidConfig | AppError::LogicCorruption => {
// Log as critical and terminate
terminate_process();
}
}
}
The Diagnostic Strategy
The key to management is classification at the boundary. When your functions return Result<T, E>, force yourself to categorize the error as soon as it crosses your service boundary. If you treat a LogicCorruption error as Transient, your system will attempt to "retry" a state that is fundamentally broken, leading to silent failures or "zombie" processes. Conversely, treating a NetworkTimeout as Fatal makes your system fragile and prone to unnecessary downtime.
By strictly separating your retry loops from your shutdown sequences, you ensure that your system remains robust against external noise while staying safe from internal failures.
Key Takeaway: Categorize errors by their solvability; if it can be fixed by waiting, it is transient; if it requires a change in state or logic, it is fatal.