datahaven-xyz/datahaven · error · Error
InvalidGateway
InvalidGateway
Error message
InvalidGateway
What it means
`process_delivery_receipt` throws `Error::InvalidGateway` when the delivery receipt's `gateway` field does not equal the pallet's configured `T::GatewayAddress`. Just as with inbound messages, outbound delivery receipts must originate from the canonical Gateway contract on Ethereum; mismatched receipts are rejected before nonce/channel bookkeeping.
Solutions
- Verify the receipt's gateway against the runtime's configured GatewayAddress; re-fetch the receipt from the correct chain if they differ.
- Fix the relayer configuration so it subscribes to Gateway events on the correct Ethereum network/contract.
- If the Gateway address changed legitimately, ship a runtime upgrade updating `T::GatewayAddress`.
Example fix
// before let receipt = fetch_receipt_from(sepolia_gateway); pallet.process_delivery_receipt(channel, relayer, receipt)?; // InvalidGateway // after assert_eq!(receipt.gateway, <Runtime as Config>::GatewayAddress::get()); pallet.process_delivery_receipt(channel, relayer, receipt)?;
Defensive patterns
Strategy: validation
Validate before calling
if (receipt.gateway.toLowerCase() !== expectedGatewayAddress.toLowerCase()) throw new Error('receipt gateway mismatch'); Type guard
function isValidReceipt(receipt, expected) { return receipt && typeof receipt.gateway === 'string' && receipt.gateway.toLowerCase() === expected.toLowerCase(); } Try / catch
try { pallet.process_delivery_receipt(channel, relayer, receipt); } catch (e) { if (matches!(e, Error::InvalidGateway)) { quarantineReceipt(receipt); } else { throw e; } } Prevention
- Subscribe to Gateway events on the correct network only
- Validate receipt.gateway against runtime config pre-dispatch
- Keep relayer chain config and runtime constants in sync
- Watch for config drift after Gateway redeploys
When it happens
Trigger: Calling `process_delivery_receipt(channel_id, relayer, receipt)` where `receipt.gateway` (H160) differs from `T::GatewayAddress::get()` — a receipt from the wrong Ethereum network, a fork, or a re-deployed Gateway contract.
Common situations: Relayer listening to a testnet while the runtime targets mainnet (or the reverse); receipts replayed from anvil/local deployments with a different Gateway address; Gateway contract redeployed on Ethereum without updating the runtime constant.
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/adb43869da694eda.
Report an issue: GitHub.
Appendix: source
Thrown at operator/pallets/outbound-queue-v2/src/lib.rs:402
<PendingOrders<T>>::insert(nonce, order);
Nonce::<T>::set(nonce.checked_add(1).ok_or(Unsupported)?);
Self::deposit_event(Event::MessageAccepted { id, nonce });
Ok(true)
}
/// Process a delivery receipt from a relayer, to allocate the relayer reward.
pub fn process_delivery_receipt(
relayer: <T as frame_system::Config>::AccountId,
receipt: DeliveryReceiptOf<T>,
) -> DispatchResult
where
<T as frame_system::Config>::AccountId: From<[u8; 32]>,
{
// Verify that the message was submitted from the known Gateway contract
ensure!(
T::GatewayAddress::get() == receipt.gateway,
Error::<T>::InvalidGateway
);
let nonce = receipt.nonce;
let order = <PendingOrders<T>>::get(nonce).ok_or(Error::<T>::InvalidPendingNonce)?;
if order.fee > 0 {
// Pay relayer reward
T::RewardPayment::register_reward(&relayer, T::DefaultRewardKind::get(), order.fee);
}
<PendingOrders<T>>::remove(nonce);
Self::deposit_event(Event::MessageDeliveryProofReceived { nonce });
Ok(())View on GitHub (pinned to edcb13dbbc)