Reqwest: attempted to run an executor while another executor is already running.

Created on 11 Jun 2019  Â·  23Comments  Â·  Source: seanmonstar/reqwest

when I try to send post requests I get this error saying attempted to run an executor while another executor is already running

Most helpful comment

@seanmonstar I'm not good with async. Could you provide an example on how to use reqwest inside an actix handler? Thanks.

All 23 comments

Had also got this error, when tried to send a request from a synchronous actix-web handler.
Downgrading to the previous reqwest version helped.

There's another poor soul experiencing this: https://www.reddit.com/r/rust/comments/bz9ofq/is_there_any_platform_independent_http_client_for/

Downgrading to 0.1.17 works so probably https://github.com/seanmonstar/reqwest/pull/538 broke this.

I wouldn't recommend downgrading, it's not a bug in reqwest. It is noticing that you are making a blocking network request while inside a non-blocking future, likely a server. While that request is blocked, your server is likely hanging and being unresponsive to any other request.

The fix is to just change from the blocking reqwest client to its async client. If you're already inside a future, you can return reqwest's future.

The problem in my case is that the place I use reqwest in is the body of the method that synchronously handles the request in actix-web: there are no futures or anything similar explicitly exposed to the developers.

That's why it's confusing in my case — I'm already doing a blocking operation, by processing a request and I'm totally fine with reqwest doing another blocking operation, but it fails with the error mentioned above.

Actix's request handlers are asynchronous. Using a blocking client in them is definitely hurting the server.

The only exception is if using Actix's specific blocking helper.

Actix's request handlers are asynchronous

Either I fail to understand something or you're not entirely correct about it, let me try one more time.

Consider the basic "official" example: https://github.com/actix/examples/blob/master/basics/

It has a number of handlers introduced, some of them are labeled as "synchronous", as here:
https://github.com/actix/examples/blob/master/basics/src/main.rs#L94

and some of them are async:
https://github.com/actix/examples/blob/master/basics/src/main.rs#L97

My case if the former one, I use .to and consider the code in the handler to be synchronous — no futures or anything similar is explicitly used in the method.
There I do a number of operations — access the database and send a request using reqwest (because there's no sync version of the native actix client).

And this is what I don't understand — how can I successfully synchronously access the database in this code but cannot make a synchronous request?

The "synchronous" handlers in actix are a facade. They are "synchronous" only in that the function doesn't explicitly return a Future. They are still executed as part of the server future, so blocking operations will pause the server future.

Let me try to show what the facade is doing:

fn sync_handler() -> String {
    "Hello".into()
}

fn async_handler() -> impl Future<Item = String, Error = actix::Error> {
    futures::future::ok(sync_handler())
}

The facade does exactly this. It's just trying to make it easier for simple handlers that don't do much.

Thank you for the explanations, now it's a bit more clear.

I don't really like the current way of notifying the user about the executor issue, but not sure I know any way to make it better.

I'm running into the same issue and having to downgrade for now. I'm not sure how to get reqwest's async version working within a function that returns synchronously. The examples on docs.rs use:

let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt");
rt.block_on(response)

but I just get an error about executors.

I saw that comment. All it says is to use reqwest's async version, but idk how to get it working.

If you're seeing that error, it's being executed inside another future. Instead of returning some value directly from your function, return a future chained off the reqwest future.

I need to return a value though. Not sure what to do with this future.

Peace be with you, I have not managed to understood futures enough... I'm inside a bot based on the irc crate and inside their process_msg(…) -> error::Result<()>. I've approached this like @toovs above, but when I'm going to actually, I think something like "resolve" the reponse from reqwest, I run into:

error[E0599]: no method named `take` found for type `reqwest::async::Response` in the current scope
  --> src/urlinfo.rs:27:21
   |
27 |             if resp.take(10 * 1024).read_to_end(&mut buf).is_err() {
   |                     ^^^^
   |
   = note: the method `take` exists but the following trait bounds were not satisfied:
           `&mut reqwest::async::Response : std::io::Read`
           `&mut reqwest::async::Response : std::iter::Iterator`

error[E0599]: no method named `take` found for type `reqwest::async::Response` in the current scope
  --> src/urlinfo.rs:41:13
   |
41 |     if resp.take(10 * 1024).read_to_end(&mut buf).is_err() {
   |             ^^^^
   |
   = note: the method `take` exists but the following trait bounds were not satisfied:
           `&mut reqwest::async::Response : std::io::Read`
           `&mut reqwest::async::Response : std::iter::Iterator`

and now I'm shamelessly hijacking a closed issue to chat...h

yeah i'm also inside an irc handler

@seanmonstar Thanks for the explanations, that makes things a lot clearer. Unfortunately since the docs didn't explicitly make this clear, there's likely a lot of code bases that are already using this pattern successfully without any issues, so instead of a hard break like what happened with reqwest 0.9.18, it would be much nicer to give a warning and a migration path. The sync code is not easy to migrate to async - fortunately we have the async/await landing shortly, but still a couple of Rust releases away.

@seanmonstar I'm not good with async. Could you provide an example on how to use reqwest inside an actix handler? Thanks.

This should never have been shipped in a patch-level release. "Helping developers find blocking client in future" is a good thing, but not if semver guarantees non-breaking changes.

No matter what you think, '_it's not a bug in reqwest_' is not a valid reasoning. If you break the contract of semver by shipping a change that breaks previous code, even if you deem it incorrect or borderline dangerous, that's still a breaking change and therefore this warrants a respective version change.

Ignoring these basic contracts undermines trust in the whole crate ecosystem and is extremely counter-productive as time is literally wasted on investigating these breaking changes.

After doing a lot of tests, I finally found a (simple) solution using the asynchronous version of Client, which makes me wonder why an example was not provided (the codes in the examples folder/ didn't really help me).
Here are my functions (working with reqwest 0.9.20):

use futures::Future;
use lazy_static::lazy_static;
use reqwest::IntoUrl;
use reqwest::r#async::Client as HttpClient;

// --snip--

fn search(word: &str) -> Result<Vec<Definition>, String> {
    let url = format!("https://api.urbandictionary.com/v0/define?term={}", word);
    get_defs(&url).wait()
}

fn get_defs<T: IntoUrl>(url: T) -> impl Future<Item=Vec<Definition>, Error=String> {
    lazy_static! {
        static ref HTTP_CLIENT: HttpClient = HttpClient::new();
    }
    HTTP_CLIENT.get(url).send()
        .and_then(|mut resp| resp.json())
        .map(|json: Json| json.list)
        .map_err(|error| format!("Erreur lors de la requête: {:?}", error))
}

Just that !
Json and Definition are just 2 struct which implement Deserialise and Debug:

#[derive(Deserialize, Debug)]
struct Json {
    list: Vec<Definition>,
}

#[derive(Deserialize, Debug)]
struct Definition {
    definition: String,
    permalink: String,
    example: String,
}

For those who are interested, I use these functions in an irc bot (based the irc crate). So it might help @quite and @toovs ;-)

I hope it will help, especially since I've seen some of you stay on reqwest version 0.9.17. I'm quite new to the Rust language, so even more so with Future, so maybe my solution is not that good.

@seanmonstar looking at the comments and references in this thread, it seems a lot of people are choosing to downgrade their reqwest dependency rather than deal with this error.

I understand that the spirit and intention of this change was to discourage poor practices w.r.t. blocking from async contexts. However, I don't think it's appropriate to force people to use the async client - at least, not at this time.

For lots of existing synchronous code, porting to async without stable async/await requires rewriting huge amounts of code that otherwise works perfectly well (albeit with potential perf issues). To echo @19h: I shouldn't be forced to make such changes just to so I can use the latest patch-release of this dependency.

Is there some technical limitation that forced this change?

While I still believe this was originally fixing a bug reqwest (it invoked an executor but forgot to call executor::enter()), I've published v0.9.22 that has reverted the behavior.

This is really stumping me. Does anyone have any example of how to get a returned value from reqwest in an actix thread?

@seanmonstar I'm not good with async. Could you provide an example on how to use reqwest inside an actix handler? Thanks.

yep, example is needed

Was this page helpful?
0 / 5 - 0 ratings

Related issues

apiraino picture apiraino  Â·  3Comments

yazaddaruvala picture yazaddaruvala  Â·  5Comments

nuxeh picture nuxeh  Â·  4Comments

samuela picture samuela  Â·  5Comments

richardanaya picture richardanaya  Â·  5Comments