datahaven-xyz/datahaven · error · Error

InvalidGateway

InvalidGateway

Error message

InvalidGateway

What it means

`process_message` throws `Error::InvalidGateway` when the message's `gateway` field does not match the configured `T::GatewayAddress` for the inbound queue. This is a safety check ensuring only messages emitted by the canonical Ethereum Gateway contract are processed; anything else (forged, misconfigured, or from a testnet Gateway) is rejected.

Solutions

  1. Compare `message.gateway` against the configured GatewayAddress for the target network; re-source the message from the correct chain if they differ.
  2. Ensure the Ethereum network the relayer listens to matches the Gateway address baked into the runtime; fix the relayer's RPC/contract config.
  3. If the Gateway address legitimately changed, perform a runtime upgrade updating `T::GatewayAddress`.

Example fix

// before: message from wrong network
let message = Message { gateway: sepolia_gateway, .. };
pallet.process_message(relayer, message)?; // InvalidGateway

// after: assert gateway matches runtime config before processing
assert_eq!(message.gateway, <Runtime as Config>::GatewayAddress::get());
pallet.process_message(relayer, message)?;
Defensive patterns

Strategy: validation

Validate before calling

if (message.gateway.toLowerCase() !== expectedGatewayAddress.toLowerCase()) throw new Error('gateway mismatch');

Type guard

function isFromKnownGateway(msg, expected) { return typeof msg.gateway === 'string' && msg.gateway.toLowerCase() === expected.toLowerCase(); }

Try / catch

try { pallet.process_message(relayer, message); } catch (e) { if (matches!(e, Error::InvalidGateway)) { logWrongChainMessage(message); skip(); } else { throw e; } }

Prevention

When it happens

Trigger: Calling `process_message(relayer, message)` where `message.gateway` (H160) differs from the pallet constant `T::GatewayAddress::get()` — e.g. a message captured on the wrong Ethereum network or a relayer feeding messages from a fork/mimic contract.

Common situations: Pointing the node at Ethereum testnet (Sepolia) while the runtime is configured for mainnet Gateway address (or vice versa); a relayer replaying messages from a local anvil deployment with a different Gateway contract; runtime config drift after a Gateway contract redeploy without a runtime upgrade.

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/cd3ad008ac897e49. Report an issue: GitHub.

Appendix: source

Thrown at operator/pallets/inbound-queue-v2/src/lib.rs:222

        /// Halt or resume all pallet operations. May only be called by root.
        #[pallet::call_index(1)]
        #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
        pub fn set_operating_mode(
            origin: OriginFor<T>,
            mode: BasicOperatingMode,
        ) -> DispatchResult {
            ensure_root(origin)?;
            OperatingMode::<T>::set(mode);
            Self::deposit_event(Event::OperatingModeChanged { mode });
            Ok(())
        }
    }

    impl<T: Config> Pallet<T> {
        pub fn process_message(relayer: T::AccountId, message: Message) -> DispatchResult {
            // Verify that the message was submitted from the known Gateway contract
            ensure!(
                T::GatewayAddress::get() == message.gateway,
                Error::<T>::InvalidGateway
            );

            let (nonce, relayer_fee) = (message.nonce, message.relayer_fee);

            // Verify the message has not been processed
            ensure!(!Nonce::<T>::get(nonce.into()), Error::<T>::InvalidNonce);

            // Process message
            let message_id = T::MessageProcessor::process_message(relayer.clone(), message)?;

            // Pay relayer reward if needed
            if !relayer_fee.is_zero() {
                T::RewardPayment::register_reward(
                    &relayer,
                    T::DefaultRewardKind::get(),
                    relayer_fee,

View on GitHub (pinned to edcb13dbbc)