Delegation warning in `Error::provide` docs

The docs include a statement:

Warning: We recommend implementors avoid delegating implementations of provide to source error implementations.

Which then discusses duplication for consumers who walk the error sources.

However

  • nothing (I know of) in stdlib provides Error::iter_sources() or similar
  • anyone calling Error::iter_sources().find(error::request_ref::<...>) will be unaffected by delegation - other than a possible improvement in performance if the provide implementation is in some way optimised (by the developer)

The tracking issue, doesn't mention anything about this. The new draft RFC clearly talks about error reporting, and only error reporting, in the motivation. That stance is backed up by the occasional comment on the zulip thread between Sept 2025 & Jan 2026 which mention the introduction of this warning at the same time as observability crates etc are being mentioned.

Use case 2: downcasting

I have the following situation where I am intending to make use of provide and document divergence from the above warning - but I want to fully understand the background before I make that choice.

pub enum Error {
    DeviceError(DeviceError),
    TaggingError(TaggingError),
    //...
}

pub enum DeviceError {
    NotReady(io::Error),
    UnexpectedEndOfTrack,
    //...
}

impl From<DeviceError> for io::Error {
    fn from(error: DeviceError) -> io::Error {
        match error {
            NotReady(e) => e,
            UnexpectedEndOfTrack => io::Error::new(UnexpectedEof, "..."),
        }
    }
}

I want to provide a mechanism for users to obtain the genuine underlying io::Error (as well as 3rd party wrapped error types) which is clearly different from the more main-stream conversion to io::Error provided by From

I would consider provide the correct API to use to do this:

assert_matches!(
    // not_ready is a top-level Error, not a DeviceError
    provide_value::<io::Error>(not_ready),
    Some(e) if e.raw_os_error == Some(OS_SPECIFIC)
);

assert!(provide_value::<io::Error>(end_of_track).is_none());

But enabling that would require delegating provide

Suggestion / Clarification

I see a difference between delegating data which is node-specific and likely relevant for aggregation in error reports (backtraces etc) and cases which are unique-in-tree / inherited and likely relevant for downstream logic (such as the case above).

  • am I completely missing the point?
  • is there a different API for this use case?
  • has this been discussed, decided and I missed the comment?

You can use not_ready.source().and_then(|err| err.downcast_ref::<io::Error>()) instead of provide_value::<io::Error>(not_ready), right?

Not in this case - as the io::Error is 2 levels down

not_ready.source() is Some(DeviceError::NotReady(_)

and other arms may have the io::Error 3 levels down. So I can't even expect some_error.source().and_then(Error::source).and_then((|err| err.downcast_ref::<io::Error>()) which is kind of hideous anyway as a public API for "get the real OS error from any of this crates error types"

there is <dyn Error>::sources()