How do I spawn an async task from an actix_web handler?

I have a fairly simple web server using the actix-web (3.3.3) crate.

It's working great for serving up requests for quick queries using a postgres database back end.

However, I now need to come up with a model to handle long running tasks. Since actix-web is uses the latest tokio runtime, I would like to be able to spawn off tokio worker tasks and use their futures (marshaled into some kind of token) for use in polling. Or perhaps even the task could be aborted if it's taking too long.

I thought spawning a tokio task would be a rather simple step from an async handler, however I'm running into problems getting a "toy" example working.

For that example, I thought I would start with the simple assert found here

Adding this to my Cargo.toml

actix-rt = "2.6.0"

and the added following line into my handler:

assert_eq!(actix_rt::spawn(async { 1 }).await.unwrap(), 1);

However, when I try out the endpoint for the handler, this is showing up in my logs:

thread 'actix-rt:worker:0' panicked at '`spawn_local` called from outside of a `task::LocalSet`', /home/chris/.cargo/registry/src/

Any ideas how to resolve this?

Many thanks in advance for any thoughts or advice on this.

Your problem is that your crate dependency tree has two copies of actix-rt in it, neither of which knows about the other. actix-web 3.3.3 depends on actix-rt ^1.1.1, but you are manually pulling in actix-rt 2.6.0. Since 2.6.0 does not satisfy the semver requirement ^1.1.1, Cargo duplicates the dependency.

The way to avoid this kind of thing is to not depend on actix-rt directly, but use the version that's re-exported by actix-web under actix_web::rt, e.g. actix_web::rt::spawn(...). That's guaranteed to be the exact same version of the library as actix-web is using.


I had in fact tried this, but I get an ugly looking compiler error that scared me away.

I just tried this form (with no actix_rt referenced in my Cargo.toml.

 assert_eq!(actix_web::rt::spawn(async { 1 }).await.unwrap(), 1);

This fails to compile with:

error[E0271]: type mismatch resolving `<impl std::future::Future<Output = [async output]> as std::future::Future>::Output == ()`
  --> src/rest/
33 |     assert_eq!(actix_web::rt::spawn(async { 1 }).await.unwrap(), 1);
   |                ^^^^^^^^^^^^^^^^^^^^ expected `()`, found integer
note: required by a bound in `actix_web::actix_rt::spawn`
  --> /home/chris/.cargo/registry/src/
28 |     F: futures_util::future::Future<Output = ()> + 'static,
   |                                     ^^^^^^^^^^^ required by this bound in `actix_web::actix_rt::spawn`

error[E0277]: `()` is not a future
  --> src/rest/
33 |     assert_eq!(actix_web::rt::spawn(async { 1 }).await.unwrap(), 1);
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
   = help: the trait `std::future::Future` is not implemented for `()`

It so happens this same error happens with the actix_rt::spawn and

actix-rt = "^1.1.1"

Is added to Cargo.toml.

It appears that the actix_rt::spawn is different than the tokio::spawn. This is currently having me interested in switching to axum which doesn't bury tokio complexities so much. But if there's something easy I'm missing on how to resolve this I'd appreciate a pointer. (i.e. that compiler error has my mystified and thinking I'm getting in deeper than I wish to actix_web internals.)

fwiw/ Some testing I've done with spawning directly from tokio using the latest version of tokio has no problems with axum.

I believe it's because actix_web::rt::spawn does not allow you to return a value from the background task, nor does it give you a way to wait for the background task to complete.

Thanks for that. Well that doesn't sound very nice of it!

( I say this, as I am busily am re-factoring my application to use axum. )

1 Like

Fwiw, this limitation seems to be lifted in the actix-web v4.0.0 release candidates.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.