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

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

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 3
  • Comments: 23 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@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.

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.

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.

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

@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?

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.

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.