{"record":{"id":"1aac9d74cad343f7","repo":"datahaven-xyz/datahaven","slug":"invalidnonce","errorCode":"InvalidNonce","errorMessage":"InvalidNonce","messagePattern":"InvalidNonce","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"operator/pallets/inbound-queue-v2/src/lib.rs","lineNumber":230,"sourceCode":"            ensure_root(origin)?;\n            OperatingMode::<T>::set(mode);\n            Self::deposit_event(Event::OperatingModeChanged { mode });\n            Ok(())\n        }\n    }\n\n    impl<T: Config> Pallet<T> {\n        pub fn process_message(relayer: T::AccountId, message: Message) -> DispatchResult {\n            // Verify that the message was submitted from the known Gateway contract\n            ensure!(\n                T::GatewayAddress::get() == message.gateway,\n                Error::<T>::InvalidGateway\n            );\n\n            let (nonce, relayer_fee) = (message.nonce, message.relayer_fee);\n\n            // Verify the message has not been processed\n            ensure!(!Nonce::<T>::get(nonce.into()), Error::<T>::InvalidNonce);\n\n            // Process message\n            let message_id = T::MessageProcessor::process_message(relayer.clone(), message)?;\n\n            // Pay relayer reward if needed\n            if !relayer_fee.is_zero() {\n                T::RewardPayment::register_reward(\n                    &relayer,\n                    T::DefaultRewardKind::get(),\n                    relayer_fee,\n                );\n            }\n\n            // Mark message as received\n            Nonce::<T>::set(nonce.into());\n\n            // Emit event with the message_id\n            Self::deposit_event(Event::MessageReceived { nonce, message_id });","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/datahaven-xyz/datahaven/blob/edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec/operator/pallets/inbound-queue-v2/src/lib.rs#L212-L248","documentation":"`process_message` throws `Error::InvalidNonce` when `Nonce::<T>::get(nonce)` is already set, meaning a message with this nonce was previously processed. The pallet uses nonces for exactly-once delivery; replaying an already-handled message is rejected to prevent double-crediting of assets or double-execution.","triggerScenarios":"Calling `process_message` with a message whose nonce already exists in the `Nonce` storage map — i.e. a replayed or duplicated message after the original was successfully processed.","commonSituations":"A relayer resubmitting the same message after a timeout without knowing the first submission succeeded; replaying historical Gateway events after a node resync; test harnesses reusing fixture messages across runs without resetting state.","solutions":["Check `Nonce::<T>::get(nonce)` (or the public nonce query) before submitting; skip the message if it is already marked processed.","Configure the relayer to track successfully delivered nonces persistently and deduplicate retries.","In tests, use fresh nonces per run or reset pallet storage between runs."],"exampleFix":"// before: blind resubmit causes InvalidNonce\npallet.process_message(relayer, message.clone())?;\npallet.process_message(relayer, message)?; // replay\n\n// after: dedupe by nonce\nif Nonce::<Runtime>::get(message.nonce) { return Ok(()); } // already processed\npallet.process_message(relayer, message)?;","handlingStrategy":"validation","validationCode":"if (await api.query.inboundQueueV2.nonce(message.nonce)) { console.log('already processed'); return; }","typeGuard":"function isUnprocessed(nonce, processedSet) { return !processedSet.has(nonce.toString()); }","tryCatchPattern":"try { pallet.process_message(relayer, message); } catch (e) { if (matches!(e, Error::InvalidNonce)) { markDelivered(message.nonce); return Ok(()); } else { throw e; } }","preventionTips":["Persist delivered nonces in relayer state across restarts","Treat InvalidNonce as idempotent-success, not failure","Use fresh nonces in test fixtures","Deduplicate events before dispatch"],"tags":["substrate","bridge","nonce","replay-protection","relayer"],"backgroundTag":"invalid-state-transition","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"}