An error owes two different messages to two different readers.
Debug — for you, in a log or a test failure. Derive it. It shows the structure, including field names, and is never shown to a user.
Display — for a human, in a log line or an API response. Write it by hand. One sentence, lowercase, no trailing period, no "Error:" prefix (the caller adds context around it).
impl fmt::Display for TimeoutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "request timed out after {}ms", self.ms)
}
}
