datahaven-xyz/datahaven · error · Error
DeferPeriodIsOver
DeferPeriodIsOver
Error message
DeferPeriodIsOver
What it means
Thrown by the `cancel_deferred_slash` root extrinsic in `pallet-external-validator-slashes` when the given era is outside the slash defer period. A slash can only be cancelled while `active_era < era <= active_era + SlashDeferDuration + 1`; before or after that window the cancellation is rejected.
Solutions
- Compute the valid window and cancel slashes for an era where `active_era < era <= active_era + SlashDeferDuration + 1`.
- Increase `SlashDeferDuration` in the runtime config if the operational window is too short.
- Query current `active_era` before invoking and pick the correct era parameter.
- If the slash already applied, use the force-inject/refund mechanism instead of cancellation.
Example fix
// before: blind call cancel_deferred_slash(origin, era, indices); // after: check window first let active = T::EraIndexProvider::active_era().index; ensure!(era > active && era <= active + defer_duration + 1); cancel_deferred_slash(origin, era, indices);
Defensive patterns
Strategy: validation
Validate before calling
const activeEra = await api.query.staking.activeEra();
const defer = await api.consts.externalValidatorSlashes.slashDeferDuration();
if (!(era > activeEra.index && era <= activeEra.index + defer + 1)) {
throw new Error(`era ${era} is outside the defer window`);
} Type guard
function isWithinDeferWindow(era, activeEra, deferDuration) {
return era > activeEra && era <= activeEra + deferDuration + 1;
} Try / catch
try {
await api.tx.externalValidatorSlashes.cancelDeferredSlash(era, indices)
.signAndSend(rootSigner);
} catch (e) {
if (String(e).includes('DeferPeriodIsOver')) {
console.error('Defer window elapsed; use force-inject/refund path instead');
}
} Prevention
- Always read activeEra from storage immediately before the call
- Set SlashDeferDuration large enough for the operational response window
- Automate slash cancellation early in the defer window, not at its end
When it happens
Trigger: Calling `cancel_deferred_slash(origin, era, slash_indices)` with `era <= active_era` (already consumed/visible) or `era > active_era + SlashDeferDuration + 1` (a future slash that is not yet in its defer window).
Common situations: Governance/root calling the extrinsic after the defer duration already elapsed, mis-typing the era index, or a misconfigured `SlashDeferDuration` (e.g. 0) making the window too short.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13).
Data as JSON: /api/errors/f5a519c49578df0a.
Report an issue: GitHub.
Appendix: source
Thrown at operator/pallets/external-validator-slashes/src/lib.rs:367
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Cancel a slash that was deferred for a later era
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32))]
pub fn cancel_deferred_slash(
origin: OriginFor<T>,
era: EraIndex,
slash_indices: Vec<u32>,
) -> DispatchResult {
ensure_root(origin)?;
let active_era = T::EraIndexProvider::active_era().index;
// We need to be in the defer period
ensure!(
era <= active_era
.saturating_add(T::SlashDeferDuration::get().saturating_add(One::one()))
&& era > active_era,
Error::<T>::DeferPeriodIsOver
);
ensure!(!slash_indices.is_empty(), Error::<T>::EmptyTargets);
ensure!(
is_sorted_and_unique(&slash_indices),
Error::<T>::NotSortedAndUnique
);
// fetch slashes for the era in which we want to defer
let mut era_slashes = Slashes::<T>::get(era);
let last_item = slash_indices[slash_indices.len().saturating_sub(1)];
ensure!(
(last_item as usize) < era_slashes.len(),
Error::<T>::InvalidSlashIndexView on GitHub (pinned to edcb13dbbc)