{"record":{"id":"20e6b68fc4ede75c","repo":"datahaven-xyz/datahaven","slug":"invalidslashindex","errorCode":"InvalidSlashIndex","errorMessage":"InvalidSlashIndex","messagePattern":"InvalidSlashIndex","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"operator/pallets/external-validator-slashes/src/lib.rs","lineNumber":383,"sourceCode":"\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);\n            Ok(())\n        }\n\n        #[pallet::call_index(1)]\n        #[pallet::weight(T::WeightInfo::force_inject_slash())]\n        pub fn force_inject_slash(\n            origin: OriginFor<T>,\n            era: EraIndex,","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/datahaven-xyz/datahaven/blob/edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec/operator/pallets/external-validator-slashes/src/lib.rs#L365-L401","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncancel_deferred_slash(origin, era, indices);\n// after\nlet era_slashes = Slashes::<T>::get(era);\nlet valid: Vec<_> = indices.into_iter().filter(|i| (*i as usize) < era_slashes.len()).collect();\ncancel_deferred_slash(origin, era, valid);","handlingStrategy":"validation","validationCode":"const eraSlashes = await api.query.externalValidatorSlashes.slashes(era);\nconst valid = slashIndices.filter(i => i < eraSlashes.length);\nif (valid.length !== slashIndices.length) {\n  console.warn('dropping out-of-range slash indices', slashIndices.length - valid.length);\n}","typeGuard":"function indicesInBounds(indices, listLength) {\n  return indices.every(i => i >= 0 && i < listLength);\n}","tryCatchPattern":"try {\n  await cancelDeferredSlash(era, indices);\n} catch (e) {\n  if (String(e).includes('InvalidSlashIndex')) {\n    await refetchEraSlashesAndRetry(era);\n  }\n}","preventionTips":["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"],"tags":["slashing","substrate","index-out-of-range"],"backgroundTag":"index-out-of-range","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"}