Debug why a Hyperlane message is not being processed...
0xa454...) or explorer URL| Parameter | Required | Example | Description |
|---|---|---|---|
message_id |
Yes | 0xa454559c... |
The 66-character hex message ID |
explorer_url |
Optional | https://explorer.hyperlane.xyz/message/0x... |
Can extract message_id from URL |
Fetch the explorer page to get origin/destination chains and basic status:
WebFetch: https://explorer.hyperlane.xyz/message/[MESSAGE_ID]
Prompt: Extract message status, origin chain, destination chain, sender, recipient, timestamp, delivery status
Key info to extract:
Use the gcloud CLI to find logs related to this message in the omniscient relayer:
gcloud logging read 'resource.type="k8s_container" AND resource.labels.project_id="abacus-labs-dev" AND resource.labels.location="us-east1-c" AND resource.labels.cluster_name="hyperlane-mainnet" AND resource.labels.namespace_name="mainnet3" AND labels.k8s-pod/app_kubernetes_io/component="relayer" AND labels.k8s-pod/app_kubernetes_io/instance="omniscient-relayer" AND labels.k8s-pod/app_kubernetes_io/name="hyperlane-agent" AND "[MESSAGE_ID]"' --project=abacus-labs-dev --limit=50 --format=json --freshness=1d
Look for the message in PendingMessage entries. Common statuses:
| Status | Meaning | Priority |
|---|---|---|
Retry(ErrorEstimatingGas) |
Gas estimation failing - contract revert | HIGH |
Retry(GasPaymentRequirementNotMet) |
Insufficient gas payment | MEDIUM |
Retry(CouldNotFetchMetadata) |
Validator signatures unavailable | LOW (check after 5+ min) |
FirstPrepareAttempt |
Still processing, not stuck yet | LOW |
Extract status with:
grep -o "message_id: [MESSAGE_ID][^}]*" [log_output] | sort -u
If message is slow/stuck, search for gas payment evaluation logs:
gcloud logging read '[BASE_RELAYER_QUERY] AND "[MESSAGE_ID]" AND jsonPayload.fields.message:"Evaluating if message meets gas payment requirement"' --project=abacus-labs-dev --limit=5 --format=json --freshness=7d
Key fields in jsonPayload.fields:
current_payment.gas_amount - gas units paid for by sendertx_cost_estimate.gas_limit - gas units needed for deliverycurrent_expenditure.gas_used - gas already spent on retriespolicy - subsidy policy (e.g., fractional_numerator: 1, fractional_denominator: 2 = 50% subsidy)If gas_amount < gas_limit, message fails with "Repreparing message: Gas payment requirement not met" and retries every ~3 minutes.
If status is ErrorEstimatingGas, the actual revert reason is in jsonPayload.fields.error. Use this query and extraction:
# Query logs with error field
gcloud logging read 'resource.type="k8s_container" AND resource.labels.project_id="abacus-labs-dev" AND resource.labels.location="us-east1-c" AND resource.labels.cluster_name="hyperlane-mainnet" AND resource.labels.namespace_name="mainnet3" AND labels.k8s-pod/app_kubernetes_io/component="relayer" AND labels.k8s-pod/app_kubernetes_io/instance="omniscient-relayer" AND labels.k8s-pod/app_kubernetes_io/name="hyperlane-agent" AND "[MESSAGE_ID]" AND jsonPayload.fields.error:*' --project=abacus-labs-dev --limit=5 --format=json --freshness=1d 2>/dev/null | grep -o '"error": "[^"]*"' | head -1
The error field contains the full revert reason, e.g.:
"error": "ContractError(...JsonRpcError { code: 3, message: \"execution reverted: panic: arithmetic underflow or overflow (0x11)\", data: Some(...) }...)"
Quick extraction - pipe to extract just the revert message:
... | grep -oP 'execution reverted: [^"\\]+' | head -1
Common revert patterns:
execution reverted: panic: arithmetic underflow or overflow (0x11) - Contract math errorexecution reverted: [CUSTOM_ERROR] - Custom contract revert (decode with cast 4byte)execution reverted with hex data - Decode selector with cast 4byte 0x[first4bytes]From the logs, identify:
origin: Source chaindestination: Destination chain (or domain ID like 4114 for Citrea)sender: Origin contract addressrecipient: Destination contract address (the warp route or recipient)nonce: Message sequence numberExample log format:
HyperlaneMessage { id: 0x..., nonce: 162898, origin: ethereum, sender: 0x..., destination: 4114, recipient: 0x... }
Summarize:
| Error | Meaning | Resolution |
|---|---|---|
panic: arithmetic underflow or overflow (0x11) |
Contract math error | Bug in recipient contract |
IXERC20_NotHighEnoughLimits() |
Bridge rate limit hit | Wait for limit reset |
InsufficientBalance |
Not enough tokens | Fund the contract |
Unauthorized |
Access control failure | Check permissions |
If CouldNotFetchMetadata persists > 5 minutes:
debug-validator-checkpoint-inconsistency skillIf GasPaymentRequirementNotMet:
current_payment.gas_amount vs tx_cost_estimate.gas_limitpolicy field for subsidy ratio (e.g., 1/2 = relayer covers 50%)When you see hex revert data like 0x4e487b71...:
cast 4byte 0x4e487b71
# Returns: Panic(uint256)
Common panic codes:
0x11 - Arithmetic overflow/underflow0x12 - Division by zero0x21 - Invalid enum value0x31 - Pop on empty array0x32 - Array out of boundsCommon domain IDs (destination field in logs):
1 - Ethereum42161 - Arbitrum10 - Optimism137 - Polygon4114 - CitreaCheck @hyperlane-xyz/registry or chain metadata for full mapping.
User asks: "Why isn't message 0xa454... being processed?"
0xa454...Retry(ErrorEstimatingGas)execution reverted: panic: arithmetic underflow or overflow (0x11)