A common mistake in Rust projects is mixing application and library error styles. I use thiserror for typed library errors and anyhow for top-level binaries.
Libraries expose specific variants so callers can branch by cause. Binaries add context and preserve chains for diagnostics.
When errors cross async boundaries, structured context messages become essential. I include identifiers like device ID and operation name in every wrapped error.
This pattern keeps API surfaces precise while still making CLI and service logs readable during incidents.