Task.await terminates GenServer because of timeout. How to fix?

The tasks take however long they need - no “iterations” involved.

  1. And I don’t want it to take however long it takes because a remote server might go down and what-not. And a task will then have to wait for it forever?

  2. Iterations are required because I’m polling a server.

  • When you start a task schedule a timeout with Process.send_after/2 including the task.pid in the message
  • Store the timer reference together with the task in a tuple in the Map
  • When the task completes use Process.cancel_timer/1 to cancel the timer.
  • When the timer message arrives first use Task.shutdown task, :brutal_kill to terminate the task.
1 Like

why not to use a) task.await and handle :exit b) or task.yield c) or spawn?

To make sure you do understand this correctly!

When you send an HTTP-request in the Task and have a timeout of 5 seconds in Task.await but the request takes 10 seconds to process, the request will be done until its very end. AND it will send it answer to its owner anyway. They will be in your inbox and waiting to be processed, regardless of you still want them. Also all sideeffects on the HTTP-endpoint will happen, If you really need a timout over HTTP-requests, use a HTTP library which provides you necessary control over the connection. This will also make sure that the timeout will only start AFTER the request has actuelly been send and not before even starting to construct it. Only milliseconds, but those can become significant!

  1. To point it out again, Task.await actually states:

Compatibility with OTP behaviours
It is not recommended to await a long-running task inside an OTP behaviour such as GenServer. Instead, you should match on the message coming from a task inside your GenServer.handle_info/2 callback.

  1. The only thing that times out is the await process - the task process it still running.

If the timeout is exceeded, await will exit; however, the task will continue to run.

1 Like

Ok. How can I do this:

Send an http request.

  • a) if an answer arrives within N seconds – good, process it.
  • b) if not, forget about it completely – not a big deal because I’ll send another one in M seconds. How can I process or reject a message or all messages if it/they arrives after the timeout?

Note that I need to return something from “do_work” for GenServer so that I can modify its state/data: in my case – either the same state or a state with N - M elements or .

Got it.
Should I use spawn?
What message should I handle inside GenServer.handle_info/2 for an answer coming from an http request? Which can arrive after a timeout as well.

Your HTTP library should be aware of a timeout option, use it. How your application can then differentiate between a timeout and a regular return value does totally depend on the library you use.

What you can return on a timeout does totally depend on your application.

The important bits are…

  • … to drop Task, because it involves coding a lot of stuff into your GenServer which doesn’t belong there
  • … to do not do the request in the GenServers process to not block it. How that can roughly look like has been shown to you
  1. In general, if my library doesn’t deal with timeout and or if timeout occurs in Task.await, how can I process in handle_info? How can I know that :exit has come from one of Task.await because of timeout? How can I flush all messages that’ll arive to the mailbox of GenServer after timeout from Task.await that have timed out?

  2. what do you mean by drop Task?

  3. HttpPoison does have timeout, but it’s described briefly and still I’ll need to be able to handle a timeout from Task.await.

  1. […]

Obselete after reading my next answer.

  1. what do you mean by drop Task?

Exactly this. Drop it, throw it away, do not use it, forget it exists.

Using Tasks timout in any way in your current constellation will cause you a lot of trouble. Just use bare processes as shown and explained before.

  1. HttpPoison does have timeout, but it’s described briefly and still I’ll need to be able to handle a timeout from Task.await.

If you were dropping the idea of using a Task as I told you many times before, there were no need to talk about Tasks timeout here.

HTTPoison will probably return a simple {:error, :timeout} or similar, just try it.


You can “flush” your processes inbox by simply receiving the messages and doing nothing on them, but you should be very careful when doing this to not drop important messages. In a genserver you can do similar in handle_info.


As a last note of warning. Not using HTTPoisons timeout but rather trying to build something around Tasks may get you into huge trouble by either polluting your processes inbox by irrelevant messages which do cost processing just to drop them. This is time where your GenServer is not available for more urgent requests!

Also if you do the cancellation of Tasks wrong it may happen, that HTTPoison is not able to check its worker process back to the connection pool, meaning that you will eventually run out of available connections earlier or later depending on how often a timeout happens.

Also HTTPosisons notion of a timeout is different than Tasks. Tasks timout will start after spanwing the tasks worker process, while HTTPoisons timeout will only start after the request has been prepared and it is trying to establish the connection. As I already said, there are probably only milliseconds of difference, but those may be significant.

1 Like

How? Task.forget_about(t1) → like this?

I’ve said: in general. Forget about HttpPoison. Say, I’ve implemented my own http library or found one without timeout and I’m using it instead of HttpPoison.

That’s what I’ve been doing – doesn’t help as you can see.

I’ve said: in general. Forget about HttpPoison. Say, I’ve implemented my own http library without timeout.

Thats a completely different question and does totally depend on how you have implemented your library and how much you have implemented yourself.

HTTPoison and -Potion are both just wrappers around well tested erlang libraries. And in fact both of them do handle the actual timeout, neither -Potion nor -Poison do it on their own.


Well, you have done it were? HTTPotion das have a default timeout of 8 seconds, Tasks of 5. HTTPotions timeout needs to be shorter than the tasks one to make it actually work inside a task.

or found one

Also. Since you wanted a generic answer how to handle timeout in a Task…

Short: A Task on itself does not time out, it will do it work until its very end.

Some kind of timeout will only happen if you use Task.await/1 or Task.yield/1. The former will exit the waiting process, the task itself will continue to work until it has finished its payload. A yield will return nil on timeout and your waiting process will continue to run, as well as the Task. The Task will eventually send a simple message of the form {ref, response} to its owner. This message needs to get handled.

If you start a linked task, await/1 will kill that task as well, since no linked task can exist without its parent. This though will risk that used resources by the Task won’t be freed accordingly without doing some extra lifting.


Also since network connections are potentially a strictly limited ressource you have to share not only within your application but throughout the entire system, you should try to use some kind of pool and not try to create a potentially infinite number of connections.

The libraries you found, do provide mechanisms to time out correctly, you DO NOT NEED to implement it on your own

Do you forget something you’re just read in a second?

One more time:

I’ve said: in general. Forget about HttpPoison. Say, I’ve implemented my own http library or found one without timeout and I’m using it instead of HttpPoison.

How can I know that :exit has come from one of Task.await because of timeout? How can I flush all messages that’ll arive to the mailbox of GenServer after timeout from Task.await that have timed out?

I overread this, since when I find a library which does not provide the functionality I need, I continue my search.

Even if I do not find something that fuillfils my needs, but I have to use my first find because it does have everything except the missed timeouts, I can’t tell how I’d put it in, since I can’t answer that question in general. It totally depends on how that library works, if it uses pooled or shared ressources which needs to be freed or if it doesn’t.

As a starting point, though, there are some functions available which send messages after a delay which you can specify, also you can specify a timeout in a receive. Those can be used as primitives to build your own timeout. Still you have to consider freeing up ressources.

1 Like