investigate-stuck-messages
Skill76 starsChanged 20 days agoApache-2.0View on GitHub
Query the relayer API to investigate stuck messages, their retry counts, and error reasons. Option 1: Grafana Alert URL (recommended) Option 2: Manual specification \*Either alerturl OR both appcontext and remote must be provided. If Grafana alert URL provided: **If manual app_context/remote provided:** Use the provided values directly. Check if port 9090 is already in use: If not in use, start port-forward in background: Wait a few seconds for the port-forward to establish. Look up domain IDs from the registry:
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 6 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 77
Use the provided values directly.
- Distinctive: line 81
Check if port 9090 is already in use:
- Distinctive: line 87
If not in use, start port-forward in background:
- Distinctive: line 93
Wait a few seconds for the port-forward to establish.
- Distinctive: line 97
Look up domain IDs from the registry:
- Very specific: line 134
Look up the app_context in rust/main/app-contexts/mainnet_config.json:
- Very specific: line 178
Note: Do not use grep -P as it's not available on macOS.
- Distinctive: line 224
Always use full message IDs, never truncated.
- Very specific: line 237
Port-forward fails: Check kubectl context: kubectl config current-context
- Very specific: line 239
API returns error: Check relayer pod: kubectl get pods -n mainnet3 | grep relayer
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Investigate Stuck Messages
Query the relayer API to investigate stuck messages, their retry counts, and error reasons.
When to Use
- Alert-based triggers:
- Alert: "Known app context relayer queue length > 0 for 40m"
- Any alert mentioning stuck messages in prepare queue
- High retry counts for specific app contexts
- User request triggers:
- "Why are messages stuck for [app_context]?"
- "Investigate stuck messages on [chain]"
- "What's causing the queue alert?"
- Pasting a Grafana alert URL
Input Parameters
Option 1: Grafana Alert URL (recommended)
/investigate-stuck-messages https://abacusworks.grafana.net/alerting/grafana/cdg1ro5hi4vswb/view?tab=instancesOption 2: Manual specification
/investigate-stuck-messages app_context=EZETH/renzo-prod remote=linea| Parameter | Required | Default | Description |
alert_url | No | - | Grafana alert URL (extracts app_context/remote from firing instances) |
app_context | No* | - | The app context (e.g., EZETH/renzo-prod, oUSDT/production) |
remote | No* | - | Destination chain name (e.g., linea, ethereum, arbitrum) |
environment | No | mainnet3 | Deployment environment |
*Either alert_url OR both app_context and remote must be provided.
Workflow
Step 1: Parse Input and Extract Alert Instances
If Grafana alert URL provided:
- Extract the alert UID from the URL (e.g.,
cdg1ro5hi4vswbfrom.../alerting/grafana/cdg1ro5hi4vswb/view) Query Prometheus directly for firing instances using
mcp__grafana__query_prometheus:sum by (app_context, remote)( max_over_time( hyperlane_submitter_queue_length{ queue_name="prepare_queue", app_context!~"Unknown|merkly_eth|merkly_erc20|helloworld|velo_message_module", hyperlane_context!~"rc|vanguard0|vanguard1|vanguard2|vanguard3|vanguard4|vanguard5", operation_status!~"Retry\\(ApplicationReport\\(.*\\)\\)|FirstPrepareAttempt", hyperlane_deployment="mainnet3", }[2m] ) ) > 0- Extract
app_contextandremotelabels from each result.
If manual app_context/remote provided:
Use the provided values directly.
Step 2: Setup Port-Forward to Relayer
Check if port 9090 is already in use:
lsof -i :9090If not in use, start port-forward in background:
kubectl port-forward omniscient-relayer-hyperlane-agent-relayer-0 9090 -n mainnet3 &Wait a few seconds for the port-forward to establish.
Step 3: Get Domain IDs for Chains
Look up domain IDs from the registry:
cat node_modules/.pnpm/@hyperlane-xyz+registry@*/node_modules/@hyperlane-xyz/registry/dist/chains/<chain>/metadata.json | jq '.domainId'Common domain IDs:
- ethereum: 1
- optimism: 10
- arbitrum: 42161
- polygon: 137
- base: 8453
- unichain: 130
- avalanche: 43114
Step 4: Query Relayer API
For each destination chain, query the relayer API:
curl -s 'http://localhost:9090/list_operations?destination_domain=<DOMAIN_ID>' > /tmp/<chain>.jsonThe response contains operations with:
id: Message ID (H256)operation.message.sender: Sender addressoperation.message.recipient: Recipient addressoperation.num_retries: Number of retries (higher = more stuck)operation.status: Error status (e.g.,{"Retry": "ErrorEstimatingGas"})operation.message.origin: Origin domain IDoperation.message.destination: Destination domain IDoperation.app_context: App context name
Step 5: Filter Messages by App Context
Look up the app_context in rust/main/app-contexts/mainnet_config.json:
jq '.metricAppContexts[] | select(.name == "<APP_CONTEXT>")' rust/main/app-contexts/mainnet_config.jsonFilter API results to only include messages where:
operation.message.recipientmatches one of therecipientAddressvalues for that destination domain
Important: Addresses are padded to 32 bytes (H256 format).
Step 6: Query GCP Logs for Actual Errors
Calculate log freshness based on retry count:
The relayer uses exponential backoff (see calculate_msg_backoff in rust/main/agents/relayer/src/msg/pending_message.rs):
| Retries | Backoff/retry | Cumulative Time | Freshness Flag |
| 1-4 | 5s-1min | ~2min | --freshness=1h |
| 5-24 | 3min | ~1h | --freshness=3h |
| 25-39 | 5-26min | ~5h | --freshness=12h |
| 40-49 | 30min-1h | ~12h | --freshness=24h |
| 50-60 | 2-22h | ~35h | --freshness=3d |
| 60+ | 22h+ | 35h+ | --freshness=7d |
For each message ID, query GCP logs with calculated freshness:
gcloud logging read 'resource.type=k8s_container AND resource.labels.namespace_name=mainnet3 AND resource.labels.pod_name:omniscient-relayer AND jsonPayload.span.id:<MESSAGE_ID> AND jsonPayload.fields.error:*' --project=abacus-labs-dev --limit=1 --format='value(jsonPayload.fields.error)' --freshness=<CALCULATED_FRESHNESS>Extract the human-readable error from the response using sed (macOS compatible):
echo "$raw_error" | sed -n 's/.*execution reverted: \([^"]*\)".*/\1/p' | head -1Common error patterns:
"execution reverted: Nonce already used"→ "Nonce already used""execution reverted: panic: arithmetic underflow"→ "Arithmetic underflow"
Note: Do not use grep -P as it's not available on macOS.
Step 7: Present Investigation Results
Output a detailed summary table with full message IDs and both error sources:
## Investigation Results for [APP_CONTEXT]
### Summary
- Total stuck messages: X
- Destinations affected: [list]
- Reprepare reasons: ErrorEstimatingGas (N), CouldNotFetchMetadata (M)
### Messages
| Message ID | Retries | Reprepare Reason | Error | Origin |
|------------|---------|------------------|-----------|--------|
| `0xaa18ebc1c79345e6d24984a0b9a5ab66c968d128d46b2357b641e56e71b8d30c` | 47 | ErrorEstimatingGas | Nonce already used | optimism |
| `0xd6aeef7c092a88aa23ad53227aeb834ae731d059b3ce749db8451e761f3f15ac` | 47 | ErrorEstimatingGas | Nonce already used | arbitrum |
**Important**: Always show the full 66-character message ID (0x + 64 hex chars). Do not truncate.
### Error Analysis
[Explain based on the actual log errors found]
### Next Steps
To denylist these messages, run:
/denylist-stuck-messages <message_ids> app_context=APP_CONTEXTColumn definitions:
- Reprepare Reason: From
operation.statusin relayer API (e.g., ErrorEstimatingGas, CouldNotFetchMetadata) - Error: Actual revert reason from GCP logs (e.g., "Nonce already used", "Arithmetic underflow")
Step 8: Output Denylist Command
At the end of the investigation results, output the full denylist command:
### Next Steps
To denylist, run:
/denylist-stuck-messages 0xaa18ebc1c79345e6d24984a0b9a5ab66c968d128d46b2357b641e56e71b8d30c 0xd6aeef7c092a88aa23ad53227aeb834ae731d059b3ce749db8451e761f3f15ac app_context=APP_CONTEXTAlways use full message IDs, never truncated.
Error Status Reference
| Status | Meaning | Action |
ErrorEstimatingGas | Gas estimation failed (contract revert) | Usually denylist - contract won't accept |
CouldNotFetchMetadata | Can't get ISM metadata | Check validators, may resolve itself |
ApplicationReport(...) | App-specific error | Check the specific error message |
GasPaymentNotFound | No IGP payment | May need manual relay with gas |
Error Handling
- Port-forward fails: Check kubectl context:
kubectl config current-context - No messages found: Queue may have cleared; alert may be stale
- API returns error: Check relayer pod:
kubectl get pods -n mainnet3 | grep relayer - App context not found: May be new/custom; ask user for sender/recipient addresses
Prerequisites
kubectlconfigured with access to mainnet cluster- Grafana MCP server connected (for alert URL parsing)
In this repository49 other files it gives its agents.
- AGENTS.mdAGENTS.md
- add-registry-changesetSkill
- build-docker-imageSkill
- classify-onchain-ownerSkill
- code-reviewSkill
- debug-messageSkill
- debug-rpc-providerSkill
- debug-validator-checkpoint-inconsistencySkill
- denylist-stuck-messagesSkill
- fetch-linear-ticketSkill
- fetch-safe-tx-batchSkill
- find-docker-imageSkill
- fix-merkle-root-mismatchSkill
- fix-rpc-urlsSkill
- gcp-logsSkill
- hyperlane-message-utilsSkill
- hyperlane-utilsSkill
- inline-pr-commentsSkill
- investigate-merkle-root-mismatchSkill
- registry-yaml-sort-policySkill
- security-reviewSkill
- self-relay-hyperlane-messageSkill
- set-moonpay-standing-quotesSkill
- start-http-registrySkill
- stop-http-registrySkill
- svm-warp-alt-manageSkill
- svm-warp-program-upgradeSkill
- tob-reviewSkill
- verify-warp-contractsSkill
- warp-balancesSkill
- warp-deploy-fund-deployerSkill
- warp-deploy-init-routeSkill
- warp-deploy-nexusSkill
- warp-deploy-register-routeSkill
- warp-deploy-select-keysSkill
- warp-deploy-send-testSkill
- warp-deploy-update-ownersSkill
- warp-deploy-validate-ownersSkill
- warp-forkSkill
- warp-key-value-expansionSkill
- warp-manage-offchain-quotesSkill
- warp-route-checkSkill
- warp-run-logSkill
- warp-simulate-svm-txsSkill
- warp-update-extendSkill
- warp-update-proposeSkill
- warp-update-resolve-artifactsSkill
- warp-updateSkill
- warp-verify-onchain-configSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

