Question: How do I make an inter_canister call from the post upgrade hook?

I am trying to make an inter-canister call to call another canister on a successful upgrade. However, it looks like post_upgrade cannot be async. This is the error that I’m seeing:

#[post_upgrade] must be above a function.
expected one of: for, parentheses, fn, unsafe, extern, identifier, ::, <, square brackets, *, &, !, impl, _, lifetime

How do I achieve my desired outcome? Thoughts?
@roman-kashitsyn @chenyan

Maybe use the new timer api to call your function after 1 nano second…it should be the next thing called most of the time. You can add a halt blocker if you need a guarantee.


This might work. Let me try.

Although, seems to be a total hack.

What’s a “halt blocker”?


Just do a check of a bool at the top of every update function. Set it to false at the end of your post upgrade and true at the end of your timer.

It does not work. Basically, a timer expects a function that doesn’t return anything whereas an async fuction returns a type of Future. The function has to be an async function because inter canister calls are async and need to be await-ed.

This is the error message I see:

error: expected fn() -> impl Future<Output = ()> {add_one} to be a fn item that returns (), but it returns impl Future<Output = ()>
label: required by a bound introduced by this call

Any other ideas? Curiously, does this workaround work in Motoko?


1 Like

Use spawn.

1 Like

Thank you. This worked. Although there’s a minor caveat.

Calling spawn directly from the post_upgrade doesn’t work. It generates the following error.

“ic0_call_new” cannot be executed in init mode

However, calling the timer with a nanosecond delay and using spawn with the async function inside it works. Like this:

ic_cdk::timer::set_timer(Duration::from_nanos(1), || ic_cdk::spawn(add_one()));
1 Like

This has come up in other threads too:

I think @icme was proposing that there could be new lifecycle hooks for this purpose:

1 Like

I think the caveat with this approach based on the threads I linked to is that it doesn’t guarantee that other messages aren’t processed before your timer function is called.

That might be fine for this use case depending on what you want to do.


Yep, this is a HACK. But it gets me unstuck for now.

Would definitely appreciate a well thought out alternative, though.

Put it another way, perhaps if all messages were to be dropped for all update calls to the canister, that might work?

I will make a change to dos(GitHub - icdev2dev/dos: Some Denial of Service Defenses for SmartCanisters on IC) to add a proposal type of ‘*’ to accommodate this.

The query calls would still need guards.