4

I'm trying to pass some custom error messages to the frame_system::note_applied_extrinsic() but it seems to be ignoring my message and always using an empty string "".

Adding this test to the pallet-template test.rs I am able to reproduce it:

use crate::{mock::*, Error, Event};
use frame_support::{assert_noop, assert_ok};
use frame_support::dispatch::{DispatchErrorWithPostInfo, DispatchInfo, Pays, PostDispatchInfo, Weight};
use sp_runtime::DispatchError;
use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidityError};

#[test]
fn other_dispatch_error_not_working() {
    new_test_ext().execute_with(|| {
        let e = DispatchError::Other(TransactionValidityError::Invalid(InvalidTransaction::Payment).into());
        let d = Err(DispatchErrorWithPostInfo {
            post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes },
            error: e,
        });
        let dispatch_info = DispatchInfo { weight: Weight::zero(), ..Default::default() };

        System::set_block_number(1);
        System::note_applied_extrinsic(&d, dispatch_info);

        let last_events = System::events();

        let err_msg = match last_events[0].event {
            RuntimeEvent::System(frame_system::Event::ExtrinsicFailed { dispatch_error: e, dispatch_info: _ }) => Some(e),
            _ => None,
        };
        assert_eq!(Some(e), err_msg);
    });
}

Here is the cargo t -p pallet-template output:

---- tests::other_dispatch_error_not_working stdout ----
thread 'tests::other_dispatch_error_not_working' panicked at 'assertion failed: `(left == right)`
  left: `Some(Other("Inability to pay some fees (e.g. account balance too low)"))`,
 right: `Some(Other(""))`', bin/node-template/pallets/template/src/tests.rs:51:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

So, why frame_system is "ignoring" the Some(Other("Inability to pay some fees (e.g. account balance too low)"))?

2 Answers 2

3

The reason goes back to the fact that DispatchError::Other has a special annotation that nullifies the encoding of the inner &'static str:

pub enum DispatchError {
    /// Some error occurred.
    Other(
        #[codec(skip)]
        #[cfg_attr(feature = "std", serde(skip_deserializing))]
        &'static str,
    ),
    ...
} 

And, because in the code path that you have shown, we write the DispatchError as a part of System::events into storage, we encode/decode it, which causes the inner string to be "lost" in the process.

The underlying reason for this is to save space in the wasm blob, and in the events vector, among a few others places. Moreover, DispatchError::Other is rarely used from within FRAME, given that most pallets use a custom enum Error.

2

Back in the old days the error was just a string, that we also did not stored on chain. Then FRAME added support for custom pallet errors. To stay compatible with th old code the Other variant was introduced that can be constructed from a &'static str. As Kian already said we don't store the string on chain to not "pollute" the state. While events are deleted at the beginning of each new block, archive nodes still store them for each block.

To solve your problem, you should start using a custom error variant and then you can check for it in your test.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.