Candid errors are so hard to decipher

Could we possible make the Candid serialization/deserialization errors easier to read? I would love to just see the field names in the errors, but having the numbers/hashes or whatever they are is very difficult to follow. Here’s an example error that was very tiring to decipher (and I haven’t yet). The Caused by section has a lot of hard-to-read numbers instead of actual field names:

Error: The Replica returned an error: code 5, message: "Canister rwlgt-iiaaa-aaaaa-aaaaa-cai trapped explicitly: Custom(Fail to decode argument 0 from table0 to record {
  batch_id : nat;
  operations : vec variant {
    CreateAsset : record { key : text; content_type : text };
    UnsetAssetContent : record { key : text; content_encoding : text };
    DeleteAsset : record { key : text };
    SetAssetContent : record {
      key : text;
      sha256 : opt vec nat8;
      chunk_ids : vec nat;
      content_encoding : text;
    };
    Clear : record {};
  };
}

Caused by:
    0: input: 4449444c0b6c0280b5a6f0047decbcccc109016d026b05b4f29c9f030392c396d10604a5eb84d30905cb87adc80b06aed1c0bb0d0a6c029f93c60271c0e5eb9b05716c029f93c60271d98fc8e10c716c019f93c602716c049f93c60271e7c8eae70107a69499bb0a09d98fc8e10c716e086d7b6d7d6c000100_0502000b2f696e6465782e68746d6c09746578742f68746d6c030b2f696e6465782e68746d6c0120aedcab8965fa9d98848286e426bb489c8d5d61f024f5dc470050d99151041c0901d602086964656e74697479
       table: type table0 = record { 1_309_252_224 : nat; 2_553_486_956 : table1 }
       type table1 = vec table2
       type table10 = record {}
       type table2 = variant {
         870_791_476 : table3;
         1_780_851_090 : table4;
         2_590_061_989 : table5;
         3_104_523_211 : table6;
         3_614_451_886 : table10;
       }
       type table3 = record { 5_343_647 : text; 1_400_566_464 : text }
       type table4 = record { 5_343_647 : text; 3_425_830_873 : text }
       type table5 = record { 5_343_647 : text }
       type table6 = record {
         5_343_647 : text;
         486_188_135 : table7;
         2_808_498_726 : table9;
         3_425_830_873 : text;
       }
       type table7 = opt table8
       type table8 = vec nat8
       type table9 = vec nat
       wire_type: table0, expect_type: record {
         batch_id : nat;
         operations : vec variant {
           CreateAsset : record { key : text; content_type : text };
           UnsetAssetContent : record { key : text; content_encoding : text };
           DeleteAsset : record { key : text };
           SetAssetContent : record {
             key : text;
             sha256 : opt vec nat8;
             chunk_ids : vec nat;
             content_encoding : text;
           };
           Clear : record {};
         };
       }
    1: table0 is not a subtype of record {
         batch_id : nat;
         operations : vec variant {
           CreateAsset : record { key : text; content_type : text };
           UnsetAssetContent : record { key : text; content_encoding : text };
           DeleteAsset : record { key : text };
           SetAssetContent : record {
             key : text;
             sha256 : opt vec nat8;
             chunk_ids : vec nat;
             content_encoding : text;
           };
           Clear : record {};
         };
       }
    2: Record field operations: table1 is not a subtype of vec variant {
         CreateAsset : record { key : text; content_type : text };
         UnsetAssetContent : record { key : text; content_encoding : text };
         DeleteAsset : record { key : text };
         SetAssetContent : record {
           key : text;
           sha256 : opt vec nat8;
           chunk_ids : vec nat;
           content_encoding : text;
         };
         Clear : record {};
       }
    3: Variant field 3_614_451_886 not found in the expected type)"
4 Likes

We chain the error messages using context, so a good way to read the errors is from bottom to top. Number 3 is where the error actually happens: the message contains an unknown variant field 3_614_451_886. It cannot display the field name, because it doesn’t appear in the receiver’s type. Number 2 further shows variant names that are in the receiver’s type, but none of field name has hash 3_614_451_886. Number 1 shows where that variant type appears in the larger context. And finally number 0 shows the raw message and the decoded type table, if none of the error messages make sense so far, we can try to decode the raw message with some other tools such as didc for further investigation.

Overall, if you read the error messages from bottom to top, you can stop going further when the error already make sense for you. In this case, you can probably stop at number 3 or 2. It’s probably better to reverse the order when displaying the errors, but I’m not sure how easy/hard to do this in Rust.

2 Likes

btw, the error you see is probably related to this PR: https://github.com/dfinity/agent-rs/pull/306