#Trouble with external library's `Error`s and `anyhow`

4 messages · Page 1 of 1 (latest)

terse umbra
#

Hi there!

I'm trying to use a library to use TMDB's API: https://crates.io/crates/tmdb-api but I don't see how to get their Errors to play nice with anyhow:

use anyhow::anyhow;
use tmdb_api::{client::reqwest::ReqwestExecutor, prelude::Command};

async fn foo() -> anyhow::Result<()> {
    _ = tmdb_api::tvshow::search::TVShowSearch::new("Gravity Falls".to_owned())
        .execute(&tmdb_api::Client::<ReqwestExecutor>::new(
            "api_key".to_owned(),
        ))
        .await
        .map_err(|e| anyhow!(e).context("Could not call TMDB API"));

    Ok(())
}

fn main() {
    println!("Hello, world!");
}

The anyhow!(e) call seems to want the e (i.e. tmdb_api::error::Error) to implement Sync, and I understand (a) this is because anyhow requires it and (b) anyhow requires it because it's required for async code. When I look at the definition for tmdb_api::error::Error, I see that there's some Boxed errors that aren't bound to implement Sync — is that the problem here?

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("couldn't execute request")]
    Request {
        #[source]
        source: Box<dyn std::error::Error + Send>,
    },
    #[error("couldn't read response")]
    Response {
        #[source]
        source: Box<dyn std::error::Error + Send>,
    },
    #[error(transparent)]
    Validation(ServerValidationBodyError),
    #[error("internal server error with code {code}")]
    Server {
        code: u16,
        #[source]
        content: ServerOtherBodyError,
    },
}

Any way to make this work without introducing a ton of boilerplate on my part?

Thanks a lot ferris

#

Here's the compiler message if that's of any interest:

error[E0599]: the method `anyhow_kind` exists for reference `&Error`, but its trait bounds were not satisfied
  --> src/main.rs:10:22
   |
10 |         .map_err(|e| anyhow!(e));
   |                      ^^^^^^^^^^ method cannot be called on `&Error` due to unsatisfied trait bounds
   |
  ::: /Users/…
   |
78 | pub enum Error {
   | -------------- doesn't satisfy `tmdb_api::error::Error: Into<anyhow::Error>`, `tmdb_api::error::Error: Sync` or `tmdb_api::error::Error: anyhow::kind::TraitKind`
   |
   = note: the following trait bounds were not satisfied:
           `tmdb_api::error::Error: Into<anyhow::Error>`
           which is required by `tmdb_api::error::Error: anyhow::kind::TraitKind`
           `tmdb_api::error::Error: Sync`
           which is required by `&tmdb_api::error::Error: anyhow::kind::AdhocKind`
           `&tmdb_api::error::Error: Into<anyhow::Error>`
           which is required by `&tmdb_api::error::Error: anyhow::kind::TraitKind`
   = note: this error originates in the macro `anyhow` (in Nightly builds, run with -Z macro-backtrace for more info)


#

Cloned the tmdb_api git repo and added + Sync to the source properties in Error. This made the error in my own project go away so I'd assume I understood the problem correctly. The "any way to make this work without introducing a ton of boilerplate on my part" is still an open question 😅

analog moth
#

Sync isn't really required for async code, it's required for multithreaded code, and thus multithreaded executors (because the task can be moved between threads). A simple fix is to just convert the error to a string. Note that a Mutex<T: Send> is sync, so you could implement your own wrapper as necessary that way. The author of tmdb_api likely left off the sync bound expecting that it wouldn't be used from multithreaded code, as many databases are singlethreaded, with a single writer.