Trouble Querying Blackhole Canister: Failed to Decode Canister Response

I’m working on a project that involves querying the Blackhole canister to get the cycles balance of another canister in Rust. However, I’m encountering an issue when I try to query the Blackhole canister. The error only occurs when I pass a canister ID that has the Blackhole as a controller. When I pass a canister ID that isn’t controlled by the Blackhole, I get the expected error response.

Error Message:

(variant {Err=record {variant {CanisterError}; "failed to decode canister response as (cycles_assessment_manager::cycles_assessment_manager::CanisterStatusResponse,): Fail to decode argument 0 from table0 to record {
status : variant { Stopped; Stopping; Running };
memory_size : nat;
cycles : nat;
settings : record {
freezing_threshold : nat;
controllers : vec principal;
memory_allocation : nat;
compute_allocation : nat;
};
module_hash : opt vec nat8;
}"}})

Method:

Here is the get_canister_status method that I’m using to query the Blackhole canister:

#[derive(CandidType, Deserialize, Debug)]
struct CanisterStatusResponse {
    status: CanisterStatus,
    memory_size: Nat,
    cycles: Nat,
    settings: CanisterSettings,
    module_hash: Option<Vec<u8>>,
}

#[derive(CandidType, Deserialize, Debug)]
enum CanisterStatus {
    Running,
    Stopping,
    Stopped,
}

#[derive(CandidType, Deserialize, Debug)]
struct CanisterSettings {
    controllers: Vec<Principal>,
    compute_allocation: Nat,
    memory_allocation: Nat,
    freezing_threshold: Nat,
}

#[derive(candid::CandidType, candid::Deserialize, Debug)]
struct CanisterStatusRequest {
    canister_id: Principal,
}

#[update]
async fn get_canister_status(canister_id: Principal) -> CallResult<Vec<u64>> {
    let black_hole_canister_id = Principal::from_text("e3mmv-5qaaa-aaaah-aadma-cai").unwrap();

    let request = CanisterStatusRequest {
        canister_id: canister_id,
    };

    let status_result: CallResult<(CanisterStatusResponse,)> =
        call(black_hole_canister_id, "canister_status", (request,)).await;

    ic_cdk::print(format!("Raw response: {:?}", status_result));

    match status_result {
        Ok((canister_status,)) => {
            ic_cdk::print(format!("Decoded response: {:?}", canister_status));
            Ok(canister_status.cycles.0.to_u64_digits())
        }
        Err((rejection_code, message)) => {
            if message.contains("InvalidResponse") {
                Err((rejection_code, "Received an invalid response from the blackhole canister".to_string()))
            } else {
                Err((rejection_code, message))
            }
        }
    }
}

Steps to Reproduce:

  1. Deploy the cycles_assessment_manager canister.
  2. Call the get_canister_status method with a canister ID that has the Blackhole as a controller.
  3. Observe the error message.

Additional Information:

  • The error only occurs when I pass a canister ID that has the Blackhole as a controller.
  • When I pass a canister ID that isn’t controlled by the Blackhole, I get the expected error response.
  • I am using ic_cdk version 0.11.0

Question:

What could be causing this decoding error, and how can I resolve it?

This seems to suggest the status field is variant {running; stopped; stopping;} (lowercase) while the definition in your code has upper case variants, and I’m pretty sure candid variants are case sensitive.

I think one way to make sure your rust code has the right types for calling a canister is to use didc bind --target rs <TARGET_CANISTER_DID_FILE>

1 Like