Candid 0.9.3 ArgumentDecoder error on update guard

This is a wrapper function for ic-stable-structures that allows us to write directly to stable memory via an API.

        // memory_write
        // @todo this isnt working
        #[::ic::cdk::update(guard = "guard_creator")]
        pub fn memory_write(memory_id: u8, chunk_offset: u32, src: &[u8]) -> Result<(), String> {
            let memory = memory_get(memory_id);
            ::ic::db::memory::write(&memory, chunk_offset, &src)

and I get this error :

error: implementation of `ArgumentDecoder` is not general enough
  --> src/canister/global/src/
13 | init!();
   | ^^^^^^^ implementation of `ArgumentDecoder` is not general enough
   = note: `(u8, u32, &[u8])` must implement `ArgumentDecoder<'0>`, for any lifetime `'0`...
   = note: ...but it actually implements `ArgumentDecoder<'1>`, for some specific lifetime `'1`
   = note: this error originates in the attribute macro `::ic::cdk::update` (in Nightly builds, run with -Z macro-backtrace for more info)

Maybe I’m doing it wrong but that’s a weird error!

Here is the implementation of the guard functions

// guard_creator
// used as a guard function
pub fn guard_creator() -> Result<(), String> {
    let caller = api::caller();
    if *CREATOR == caller {
    } else {
        Err(format!("caller '{caller}' is not creator"))

// guard_controller
// NOTE: can't be used as guard right now because async
pub async fn is_controller() -> Result<(), String> {
    let caller = api::caller();
    if this::canister().has_controller(&caller).await {
    } else {
        Err("caller is not controller".to_string())

You ic_cdk is also up-to-date?

When I upgraded this crate and candid recently I faced various weird issue (documented that in a blog post) but, not the one you are mentioning. However, the one I had in the lib in which I use the stable-structures could be solved by using a PR that bumps the ic-cdk in the crate, maybe can help here too?

ic-stable-structures = { git = "", branch = "lwshang/update_cdk"}

Not the Rust expert, so it’s good possible that my answer does not help at all. Just wanted to share in case useful.

Ah another idea, did you imported (use ...:guard_creator) in your lib?

From another blog post of mine:

// ️🖖1️⃣ declare the new module
mod guards;

// 🖖2️⃣ the function needs to be imported
use crate::guards::caller_is_user;

// 🖖3️⃣ set the guard by its function's name
#[query(guard = "caller_is_user")]
fn greet(name: String) -> String {
    format!("Hello, {}!", name)

works fine on query, just has an issue on update

I’ll forward your question to the team for visibility.

1 Like

I think it’s because the memory_write update function takes a src: &[u8] parameter and the update macro decodes the candid arg and passes it to the function but candid can’t decode into a reference because there is nothing to reference after the decoding is done. So candid can only decode into owned values. (Candid can encode references though).

Try using the owned type for the parameter src: Vec<u8>.

1 Like

Ah yes, that was it. I had the candid written as vec u8 but I think I got a bit carried away with Clippy and made the endpoint a reference.

All ok now, thanks! Sorry, the error message completely threw me.