{"record":{"id":"2233264f182c48ed","repo":"datahaven-xyz/datahaven","slug":"notsortedandunique","errorCode":"NotSortedAndUnique","errorMessage":"NotSortedAndUnique","messagePattern":"NotSortedAndUnique","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"operator/pallets/external-validator-slashes/src/lib.rs","lineNumber":375,"sourceCode":"        pub fn cancel_deferred_slash(\n            origin: OriginFor<T>,\n            era: EraIndex,\n            slash_indices: Vec<u32>,\n        ) -> DispatchResult {\n            ensure_root(origin)?;\n\n            let active_era = T::EraIndexProvider::active_era().index;\n\n            // We need to be in the defer period\n            ensure!(\n                era <= active_era\n                    .saturating_add(T::SlashDeferDuration::get().saturating_add(One::one()))\n                    && era > active_era,\n                Error::<T>::DeferPeriodIsOver\n            );\n\n            ensure!(!slash_indices.is_empty(), Error::<T>::EmptyTargets);\n            ensure!(\n                is_sorted_and_unique(&slash_indices),\n                Error::<T>::NotSortedAndUnique\n            );\n            // fetch slashes for the era in which we want to defer\n            let mut era_slashes = Slashes::<T>::get(era);\n\n            let last_item = slash_indices[slash_indices.len().saturating_sub(1)];\n            ensure!(\n                (last_item as usize) < era_slashes.len(),\n                Error::<T>::InvalidSlashIndex\n            );\n\n            // Remove elements starting from the highest index to avoid shifting issues.\n            for index in slash_indices.into_iter().rev() {\n                era_slashes.remove(index as usize);\n            }\n            // insert back slashes\n            Slashes::<T>::insert(era, &era_slashes);","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/datahaven-xyz/datahaven/blob/edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec/operator/pallets/external-validator-slashes/src/lib.rs#L357-L393","documentation":"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.","triggerScenarios":"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.","commonSituations":"Merging slash index lists from multiple sources, a HashSet iteration order used directly, or duplicated event subscriptions producing the same index twice.","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."],"exampleFix":"// before\nlet indices = collected_indices;\ncancel_deferred_slash(origin, era, indices);\n// after\nlet mut indices: Vec<_> = collected_indices;\nindices.sort_unstable();\nindices.dedup();\ncancel_deferred_slash(origin, era, indices);","handlingStrategy":"validation","validationCode":"const sorted = [...slashIndices].sort((a, b) => a - b);\nconst unique = sorted.filter((v, i) => i === 0 || v !== sorted[i - 1]);\nif (unique.length !== slashIndices.length) {\n  throw new Error('slash indices must be sorted and unique');\n}","typeGuard":"function isSortedAndUnique(indices) {\n  return indices.every((v, i) => i === 0 || (v > indices[i - 1]));\n}","tryCatchPattern":"try {\n  await cancelDeferredSlash(era, indices);\n} catch (e) {\n  if (String(e).includes('NotSortedAndUnique')) {\n    await cancelDeferredSlash(era, normalize(indices));\n  }\n}","preventionTips":["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"],"tags":["slashing","substrate","input-validation","ordering"],"backgroundTag":"invalid-argument-value","analyzedSha":"edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec","analyzedAt":"2026-09-13T19:19:32.206Z","contentChangedAt":"2026-09-13T19:19:32.206Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}