tomevault-io

preserve-error-source

Use when defining a new error variant or wrapping a lower-level error — keep the underlying error's source chain intact.

tomevault-io 1 Updated 1mo ago
GitHub

Install

npx skillscat add tomevault-io/tomes/preserve-error-source

Install via the SkillsCat registry.

SKILL.md

Preserve Error Source Chains

Rule

When a new error wraps a lower-level error, preserve the source so the chain remains traversable:

  • Use thiserror's #[source] attribute (or #[from]) to attach the underlying error.
  • For dynamic sources, Box<dyn Error + Send + Sync + 'static>.
  • Do not call .to_string() on the source and embed it into the wrapper's message — that breaks Error::source() traversal and destroys structured information.

Why

Tools like anyhow, tracing, and logging walk Error::source() to render full chains and group by root cause. Stringifying the source into the message flattens it to one opaque string — the chain can't be walked and the inner error's fields are gone.

Examples

// Good
#[derive(Debug, thiserror::Error)]
pub enum AccountError {
    #[error("failed to deserialize account storage")]
    StorageDeser(#[source] DeserializationError),

    #[error("failed to load account from {path}")]
    Load { path: PathBuf, #[source] io: io::Error },
}

// Bad: source is stringified, chain is lost
#[derive(Debug, thiserror::Error)]
pub enum AccountError {
    #[error("failed to deserialize account storage: {0}")]
    StorageDeser(String),
}

// Bad: source baked into the message via format!
return Err(AccountError::StorageDeser(format!("{e}")));

Source: 0xMiden/protocol — distributed by TomeVault.