Debug why a Hyperlane message is not being processed. Use when given a message ID or explorer URL to investigate delivery failures, gas estimation errors, validator issues, or other processing problems.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add hyperlane-xyz/hyperlane-monorepo --skill debug-message --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Debug Message?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/hyperlane-xyz-debug-message)More formats (shields.io, HTML) on the badges page.
---
name: debug-message
description: Debug why a Hyperlane message is not being processed. Use when given a message ID or explorer URL to investigate delivery failures, gas estimation errors, validator issues, or other processing problems.
---
# Debug Hyperlane Message Skill
## When to Use
- User provides a message ID (e.g., `0xa454...`) or explorer URL
- Investigating why a message is stuck or not delivered
- Debugging gas estimation failures
- Understanding message processing status
## Input Parameters
| 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 |
## Debugging Workflow
### Step 1: Get Basic Message Details from Explorer
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:
- Origin chain and domain ID
- Destination chain and domain ID
- Is it delivered? (if yes, no debugging needed)
- Timestamp (how old is it?)
### Step 2: Search Relayer Logs for the Message
Use the gcloud CLI to find logs related to this message in the omniscient relayer:
```bash
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
```
### Step 3: Identify the Message Status
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:
```bash
grep -o "message_id: [MESSAGE_ID][^}]*" [log_output] | sort -u
```
### Step 4: Check for Gas Payment Issues
If message is slow/stuck, search for gas payment evaluation logs:
```bash
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 sender
- `tx_cost_estimate.gas_limit` - gas units needed for delivery
- `current_expenditure.gas_used` - gas already spent on retries
- `policy` - 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.
### Step 5: For Gas Estimation Errors - Get the Revert Reason
If status is `ErrorEstimatingGas`, the actual revert reason is in `jsonPayload.fields.error`. Use this query and extraction:
```bash
# 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:
```bash
... | grep -oP 'execution reverted: [^"\\]+' | head -1
```
Common revert patterns:
- `execution reverted: panic: arithmetic underflow or overflow (0x11)` - Contract math error
- `execution reverted: [CUSTOM_ERROR]` - Custom contract revert (decode with `cast 4byte`)
- `execution reverted` with hex data - Decode selector with `cast 4byte 0x[first4bytes]`
### Step 6: Extract Message Details
From the logs, identify:
- `origin`: Source chain
- `destination`: Destination chain (or domain ID like `4114` for Citrea)
- `sender`: Origin contract address
- `recipient`: Destination contract address (the warp route or recipient)
- `nonce`: Message sequence number
Example log format:
```
HyperlaneMessage { id: 0x..., nonce: 162898, origin: ethereum, sender: 0x..., destination: 4114, recipient: 0x... }
```
### Step 7: Report Findings
Summarize:
1. **Message ID**: Full ID
2. **Route**: Origin -> Destination (e.g., Ethereum -> Citrea)
3. **Status**: Current processing status
4. **Root Cause**: The actual error (e.g., contract revert reason)
5. **Retry Count**: How many times it's been attempted
6. **Recommendation**: What needs to happen (e.g., fix recipient contract, wait for validators)
## Common Root Causes
### Gas Estimation Errors
| 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 |
### Metadata/Validator Issues
If `CouldNotFetchMetadata` persists > 5 minutes:
1. Check validator status using the `debug-validator-checkpoint-inconsistency` skill
2. Identify if validators are behind on the origin chain
### Gas Payment Issues
If `GasPaymentRequirementNotMet`:
- Compare `current_payment.gas_amount` vs `tx_cost_estimate.gas_limit`
- Message will auto-retry every ~3 min until gas prices drop or subsidy kicks in
- Check `policy` field for subsidy ratio (e.g., 1/2 = relayer covers 50%)
- Resolution: wait for gas prices to drop, or manually subsidize via IGP top-up
## Decoding Revert Selectors
When you see hex revert data like `0x4e487b71...`:
```bash
cast 4byte 0x4e487b71
# Returns: Panic(uint256)
```
Common panic codes:
- `0x11` - Arithmetic overflow/underflow
- `0x12` - Division by zero
- `0x21` - Invalid enum value
- `0x31` - Pop on empty array
- `0x32` - Array out of bounds
## Domain ID Reference
Common domain IDs (destination field in logs):
- `1` - Ethereum
- `42161` - Arbitrum
- `10` - Optimism
- `137` - Polygon
- `4114` - Citrea
Check `@hyperlane-xyz/registry` or chain metadata for full mapping.
## Example Investigation
User asks: "Why isn't message 0xa454... being processed?"
1. Fetch explorer: Origin=Ethereum, Dest=Citrea, not delivered
2. Query relayer logs for `0xa454...`
3. Find status: `Retry(ErrorEstimatingGas)`
4. Search error logs: `execution reverted: panic: arithmetic underflow or overflow (0x11)`
5. Report: "Message from Ethereum to Citrea is failing because the recipient contract (0xbd39...) on Citrea is reverting with an arithmetic overflow error. This is a bug in the destination contract that needs to be fixed by the contract owner."
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!