1/6

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)
    }
}