Want to share your content on R bloggers? click here if you have a blog, or here if you don’t.
The future package celebrates ten years of CRAN from June 19, 2025. I got a little stuck over the holidays and attending the fantastic useR! 2025 conference, but as promised, here is the fourth in a series of blog posts highlighting recent improvements to the
future verse ecosystem.
TL; DR
In the past, futures that were paused or abruptly terminated were likely to put the future ecosystem in a corrupt state, requiring you to manually restart the future backend. This is no longer necessary;
Futures now handle evaluation breaks
Futures can now deal with abrupt, parallel layoffs of employees (“crashes”)
Crashed employees are automatically restarted
Interrupts 🛑
Below is a future that emulates an R expression that is interrupted mid-evaluation:
library(future)
f <- future({ a <- 42; rlang::interrupt(); 2 * a })
If we try to figure out the value of this future, we get:
v <- value(f) #> Error: A future () of class SequentialFuture was interrupted #> at 2025-10-15T15:37:09, while running on 'localhost' (pid 530375)
This works the same for all future backends, for example
plan(future.mirai::mirai_multisession)
f <- future({ a <- 42; rlang::interrupt(); 2 * a })
v <- value(f)
#> Error: A future () of class MiraiMultisessionFuture was
#> interrupted at 2025-10-15T15:39:17, while running on 'localhost' (pid 531734)
It’s unusual for R to interrupt himself like this, but it can happen
plan(sequential) is used and the user presses Ctrl-C
(general) or sends kill -SIGINT (less common).
There are other ways to interrupt a future – more on that in a future post.
Crashed workers 💥
Below is a future simulating a parallel worker that abruptly terminates (“crashing”) during evaluation:
library(future)
plan(multisession)
f <- future({ a <- 42; tools::pskill(Sys.getpid()); 2 * a })
Here tools::pskill(Sys.getpid()) kills the R worker process that evaluates the call. In practice, an employee can crash for various reasons. For example,
The R process may run out of memory and be terminated by the operating system (“OOM killer”)
In an HPC environment, the job scheduler can terminate the worker if it exceeds memory or runtime limits
The user can kill the worker manually (e.g.
kill -SIGQUIT) or cancel the HPC job (
scancelorqdel)
Regardless of how the parallel worker is terminated, querying the value returns:
v <- value(f) #> Error: Future () of class MultisessionFuture interrupted, #> while running on 'localhost' (pid 538181)
Technically this error is also inherited FutureInterruptErrorjust like when there is a user interruption. This behavior is consistent across all backends, although some provide more detailed error messages.
Crashed workers are automatically restarted by the future backend, meaning you no longer have to manually restart the backend to achieve the same.
Note that only the worker is restarted – the future itself is not.
Retrying an interrupted future 🔁
Regardless of the reason why a future was interrupted, you can restart it by calling first reset()after which a reevaluation is triggered via
resolved() or value().
For example, take a problematic position where the employee is stuck about 50% of the time:
problematic_fcn <- function(x) {
if (proc.time()[3] %% 1 < 0.5)
tools::pskill(Sys.getpid())
sqrt(x)
}
For example, if we enable a parallel worker, we can retry ten times before giving up:
library(future)
plan(multisession)
f <- future({ problematic_fcn(9) })
for (kk in 10:1)
tryCatch({
v <- value(f)
break
}, FutureInterruptError = function(e) {
if (kk == 1) stop(e)
message("future interrupted, retrying ...")
f <- reset(f)
})
}
message("value: ", v)
This can result in:
future interrupted, retrying ... future interrupted, retrying ... future interrupted, retrying ... value: 3
It’s a fun exercise to write a helper function future_retry() to simplify this as:
f <- future_retry({ problematic_fcn(9) }, times = 10)
message("value: ", v)
May the future be with you!
Henrik
Related
#Futures #Interruptions #Crashes #Retries #bloggers


