I am trying to split our validators of a substrate chain into signing subgroups for key signing purposes. The OneSessionHandler gives me the perfect information I need to do this. On the end of each session I get to know have the validator set changed, what it changed from to what it changed to

It seems like the event only gets propagated to pallets that are contained in the key type. I do not need another key type however I want the event to get propagated to my custom pallet.

Is there a way to get this event triggered in my custom pallet or maybe am I thinking about this wrong and there is a better way to get this information on validator changes?

Edit: Additional info

When I add the pallet types manually and add in my custom pallet straight to the config of pallet session

type SessionHandler = (Grandpa, Babe, ImOnline, AuthorityDiscovery, CustomPallet);

I get a runtime panic

022-10-12 11:55:44 Substrate Node    
2022-10-12 11:55:44 ✌️  version 3.0.0-monthly-2021-10-9ec3350-x86_64-macos    
2022-10-12 11:55:44 ❤️  by Substrate DevHub <https://github. Fm/substrate-developer-hub>, 2017-2022    
2022-10-12 11:55:44 📋 Chain specification: Development    
2022-10-12 11:55:44 🏷  Node name: foregoing-seat-4750    
2022-10-12 11:55:44 👤 Role: AUTHORITY    
2022-10-12 11:55:44 💾 Database: RocksDb at /var/folders/gr/dhyt1nqs0_v9rcrz70wgf5lr0000gn/T/substrate30JPcz/chains/dev/db/full    
2022-10-12 11:55:44 ⛓  Native runtime: node-267 (substrate-node-1.tx2.au10)    


Version: 3.0.0-monthly-2021-10-9ec3350-x86_64-macos

   0: backtrace::capture::Backtrace::new
   1: sp_panic_handler::set::{{closure}}
   2: std::panicking::rust_panic_with_hook
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panicking.rs:702:17
   3: std::panicking::begin_panic_handler::{{closure}}
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panicking.rs:586:13
   4: std::sys_common::backtrace::__rust_end_short_backtrace
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/sys_common/backtrace.rs:138:18
   5: rust_begin_unwind
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panicking.rs:584:5
   6: core::panicking::panic_fmt
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/core/src/panicking.rs:142:14
   7: std::thread::local::LocalKey<T>::with
   8: sp_state_machine::basic::BasicExternalities::execute_with_storage
   9: <entropy_runtime::GenesisConfig as sp_runtime::BuildStorage>::assimilate_storage
  10: sp_runtime::BuildStorage::build_storage
  11: <sc_chain_spec::chain_spec::ChainSpec<G,E> as sp_runtime::BuildStorage>::build_storage
  12: sc_service::client::client::Client<B,E,Block,RA>::new
  13: sc_service::builder::new_full_parts
  14: entropy::service::new_partial
  15: entropy::service::new_full_base
  16: entropy::service::new_full
  17: std::thread::local::LocalKey<T>::with
  18: tokio::park::thread::CachedParkThread::block_on
  19: tokio::runtime::thread_pool::ThreadPool::block_on
  20: sc_cli::runner::Runner<C>::run_node_until_exit
  21: entropy::command::run
  22: entropy::main
  23: std::sys_common::backtrace::__rust_begin_short_backtrace
  24: std::rt::lang_start::{{closure}}
  25: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/core/src/ops/function.rs:280:13
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panicking.rs:492:40
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panicking.rs:456:19
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panic.rs:137:14
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/rt.rs:128:48
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panicking.rs:492:40
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panicking.rs:456:19
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/panic.rs:137:14
             at /rustc/29e4a9ee0253cd39e552a77f51f11f9a5f1c41e6/library/std/src/rt.rs:128:20
  26: _main

Thread 'main' panicked at 'Number of keys in session handler and session keys does not match', /Users/jesseabramowitz/.cargo/git/checkouts/substrate-7e08433d4c370a21/c89d524/frame/session/src/lib.rs:433

This is a bug. Please report it at:

  • If possible, prefer to text snippets rather than screenshots. Text works better for discoverability of the issue by other people.
    – pepyakin
    Oct 12, 2022 at 16:02
  • thanks @pepyakin, edited Oct 12, 2022 at 16:46

1 Answer 1


You should use the SessionManager:

struct SessionManager<I>(sp_std::marker::PhantomData<I>);

impl<I: pallet_session::SessionManager<ValidatorId>, ValidatorId> pallet_session::SessionManager<ValidatorId> for SessionManager<I> {
    fn new_session(new_index: SessionIndex) -> Option<Vec<ValidatorId>> {
        let new_session = I::new_session(new_index);

        if let Some(validators) = &new_session {
            // Note the validators


    fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<ValidatorId>> {

    fn end_session(end_index: SessionIndex) { I::end_session(end_index); }
    fn start_session(start_index: SessionIndex) { I::start_session(start_index); }

Then you use this custom SessionManager and pass the original session manager I.

  • hmmm I may be missing something, I have implemented your code snippet, it compiles, but still it doesn't trigger in my custom pallet. I wonder if I need to add something in the runtime? Oct 14, 2022 at 2:55
  • Yeah for sure. You need to set this as the SessionManager of the session pallet Config trait.
    – bkchr
    Oct 14, 2022 at 19:18
  • Thank you! works now like you said just needed to add this ``` type SessionManager = custom_pallet::SessionManager<pallet_session::historical::NoteHistoricalRoot<Self, Staking>>; ``` to session config Oct 14, 2022 at 23:48
  • @bkchr I suppose you recommend the SessionManager over the OneSessionHandler since SessionManager::new_session returns the upcoming validator set which can be compared against the current before the new session starts? Also, OneSessionHandler works with the session keys of the validators, not their AccountIds. Just wondering about the decision for going with this trait. Feb 7, 2023 at 7:28

Your Answer

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

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