datahaven-xyz/datahaven · warning · Error
InvalidSlashIndex
InvalidSlashIndex
Error message
InvalidSlashIndex
What it means
Thrown by `cancel_deferred_slash` when the largest provided slash index is out of bounds for the stored `Slashes::<T>::get(era)` vector, i.e. `last_item >= era_slashes.len()`. The extrinsic validates against the highest index first so partial application can never occur.
Solutions
- Re-read `Slashes::<T>::get(era)` for the exact era right before building the index list.
- Clamp/filter indices to those `< era_slashes.len()` in the caller before submitting.
- Avoid re-running a cancellation with previously used indices; track which cancellations already executed.
- Double-check the era parameter matches the era whose indices were collected.
Example fix
// before cancel_deferred_slash(origin, era, indices); // after let era_slashes = Slashes::<T>::get(era); let valid: Vec<_> = indices.into_iter().filter(|i| (*i as usize) < era_slashes.len()).collect(); cancel_deferred_slash(origin, era, valid);
Defensive patterns
Strategy: validation
Validate before calling
const eraSlashes = await api.query.externalValidatorSlashes.slashes(era);
const valid = slashIndices.filter(i => i < eraSlashes.length);
if (valid.length !== slashIndices.length) {
console.warn('dropping out-of-range slash indices', slashIndices.length - valid.length);
} Type guard
function indicesInBounds(indices, listLength) {
return indices.every(i => i >= 0 && i < listLength);
} Try / catch
try {
await cancelDeferredSlash(era, indices);
} catch (e) {
if (String(e).includes('InvalidSlashIndex')) {
await refetchEraSlashesAndRetry(era);
}
} Prevention
- Re-read Slashes(era) fresh before building indices — never reuse cached lists
- Never rerun cancellations with previously used indices
- Verify the era parameter matches the era the indices were fetched from
When it happens
Trigger: Calling `cancel_deferred_slash` with an index referring to a slash that does not exist for that era — e.g. indices computed for a different era, stale indices after slashes were already cancelled (the vector shrank), or an off-by-one in the caller.
Common situations: Cancelling slashes for era N using indices fetched for era N-1, repeated cancellation runs reusing old indices after the slash list was mutated, or querying storage directly with stale state.
Related errors
AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13).
Data as JSON: /api/errors/20e6b68fc4ede75c.
Report an issue: GitHub.
Appendix: source
Thrown at operator/pallets/external-validator-slashes/src/lib.rs:383
// 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>::InvalidSlashIndex
);
// Remove elements starting from the highest index to avoid shifting issues.
for index in slash_indices.into_iter().rev() {
era_slashes.remove(index as usize);
}
// insert back slashes
Slashes::<T>::insert(era, &era_slashes);
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::force_inject_slash())]
pub fn force_inject_slash(
origin: OriginFor<T>,
era: EraIndex,View on GitHub (pinned to edcb13dbbc)