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

  1. Sort and deduplicate indices in the caller: `indices.sort_unstable(); indices.dedup();`
  2. Use a BTreeSet to collect indices so ordering and uniqueness are guaranteed by construction.
  3. 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

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)