io_not_allowed: a migrate_state function performs IO
An actor module’s <actor>_migrate_state function turns the state of a
running actor into the state the new code expects, during a hot code upgrade.
It must be IO-free: no capability-needing calls, directly or through the
functions it calls.
A program that triggers it
mod Counter do
needs IO.Console
actor CounterActor do
state { count : Int }
init { count: 0 }
on Inc(x : Int) do { count: state.count + x } end
end
fn counter_migrate_state(old : Int) : Int do
println("upgrading")
old + 1
end
end
The fix
Compute the new state only; log from a handler once the actor runs the new code:
mod Counter do
needs IO.Console
actor CounterActor do
state { count : Int }
init { count: 0 }
on Inc(x : Int) do { count: state.count + x } end
end
fn counter_migrate_state(old : Int) : Int do
old + 1
end
end
Why the rule exists
Migration runs inside the upgrade window, after the old code stops and before the actor takes messages again. IO there can block or fail halfway, leaving the actor neither on the old state nor the new one, and it would run again on every retry of the upgrade.
See also: the language reference.