datahaven-xyz/datahaven · warning · Error
NotSortedAndUnique
NotSortedAndUnique
Error message
NotSortedAndUnique
What it means
Thrown by `cancel_deferred_slash` when `slash_indices` is not sorted in ascending order or contains duplicates. `is_sorted_and_unique` enforces this so the removal loop can delete elements safely without index invalidation.
Solutions
- Sort and deduplicate indices in the caller: `indices.sort_unstable(); indices.dedup();`
- Use a BTreeSet to collect indices so ordering and uniqueness are guaranteed by construction.
- Validate before submission and reject/fix off-chain tooling that produces unsorted lists.
Example fix
// before let indices = collected_indices; cancel_deferred_slash(origin, era, indices); // after let mut indices: Vec<_> = collected_indices; indices.sort_unstable(); indices.dedup(); cancel_deferred_slash(origin, era, indices);
Defensive patterns
Strategy: validation
Validate before calling
const sorted = [...slashIndices].sort((a, b) => a - b);
const unique = sorted.filter((v, i) => i === 0 || v !== sorted[i - 1]);
if (unique.length !== slashIndices.length) {
throw new Error('slash indices must be sorted and unique');
} Type guard
function isSortedAndUnique(indices) {
return indices.every((v, i) => i === 0 || (v > indices[i - 1]));
} Try / catch
try {
await cancelDeferredSlash(era, indices);
} catch (e) {
if (String(e).includes('NotSortedAndUnique')) {
await cancelDeferredSlash(era, normalize(indices));
}
} Prevention
- Collect indices in a BTreeSet/sorted set by construction
- Run normalize-then-submit as a standard pre-step in tooling
- Add client-side assertion mirroring is_sorted_and_unique before submission
When it happens
Trigger: Calling `cancel_deferred_slash` with indices like `[5, 3]` or `[2, 2, 7]`. Usually the result of an off-chain collection that was never deduplicated/sorted before submission.
Common situations: Merging slash index lists from multiple sources, a HashSet iteration order used directly, or duplicated event subscriptions producing the same index twice.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13).
Data as JSON: /api/errors/2233264f182c48ed.
Report an issue: GitHub.
Appendix: source
Thrown at operator/pallets/external-validator-slashes/src/lib.rs:375
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>::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);View on GitHub (pinned to edcb13dbbc)