Debugging Messages
Message Lifecycle
Every LayerZero message goes through the following high-level steps:
-
Source Block Confirmations: The message remains pending until the source chain finalizes the required number of block confirmations. This ensures that the transaction is securely committed on the source chain.
-
DVN/Verification: Each Decentralized Verifier Network (DVN) independently verifies the message and submits an on-chain transaction attesting to its validity.
-
Committer/Commit Verification: Once all required DVN attestations are available, a Committer submits a transaction to aggregate and commit these verifications on the destination chain. This step guarantees that the message has been sufficiently validated.
-
Executor/Message Execution: Finally, an Executor submits a transaction to deliver and execute the verified message on the destination chain.
Debugging Messages using LayerZero Scan
After a LayerZero message is successfully submitted on the source chain, it can be tracked using LayerZero Scan. OApps can monitor the full message lifecycle, including delivery status and configuration details, directly through the LayerZero Scan.
For programmatic access, the LayerZero Scan Swagger API provides a comprehensive set of endpoints to query, track, and analyze cross-chain messages and transactions. With the LayerZero Scan API, you can retrieve messages using parameters such as transaction hash, OApp address, wallet address, status, pathwayId, GUID, and more.
Message Statuses Overview
Message status is an important indicator of what’s happening with your message. Always check the status first before diving deeper into debugging—it can save significant time. Below are the main statuses on LayerZero Scan:
Delivered
The message has been successfully sent and received by the destination chain.
The Delivered status indicates that the lzReceive
function was successfully invoked when the message arrived at the destination chain. However, in some cases, the subsequent lzCompose
execution may fail. If there is a Composer implemented, review the lzCompose
message status on LayerZero Scan and follow the provided instructions to retry message.
Inflight
The message is currently being transmitted between chains and has not yet reached its destination.
-
If DVN verification has not yet started, verify the number of block confirmations required on the source chain (configured in the receiveConfig). DVNs will only begin verification after the source transaction has reached the configured confirmation threshold.
-
If the required confirmations are reached but the message remains in an inflight state, the issue may fall into one of the following categories:
-
One or more DVNs have not yet submitted their verification for the message.
-
All DVNs have submitted verifications, but the Committer has not yet aggregated and committed them on the destination chain.
-
The Committer has successfully committed the verifications, but the Executor has not yet executed the message.
-
If a pathway has Ordered Execution enabled, a message cannot be executed until all preceding messages have been fully verified. Check the Message Execution Options in LayerZero Scan to confirm whether the ordered execution option is set to
true
and identify the first unverified message in the sequence, as subsequent messages will not be executed until it is verified. -
At the stage of pending execution, execution can also be triggered manually by calling on the Endpoint's
lzReceive
function. This call is permissionless and can be initiated by anyone. Alternatively, LayerZero Scan provides a built-in option to execute the message directly through its interface.
-
-
If the message remains inflight and is not delivered within the expected timeframe, contact community support for further assistance.
Failed
The message is delivered at destination chain but the message execution failed.
LayerZero Scan displays any errors encountered during message execution. If the underlying issue can be resolved, the message can then be retried through the interface.
See Message Execution for more details.
Blocked
The message is prevented from progressing due to configuration issues and requires manual intervention or updates to resolve.
A "Blocked" message usually points to configuration issues:
-
NotInitializable:
This status typically indicates that the destination OApp is either missing trusted peer settings or the pathway has not been properly initialized.
Common causes:
-
Incorrect peer configuration: Ensure that
setPeer()
is correctly called on both the source and destination chains during deployment. Double-check that the address format and endpoint ID are accurate. -
Pathway not initialized correctly: Confirm that
allowInitializePath()
is properly implemented in your OApp contract. Learn more: allowInitializePath.
-
-
Dst OApp Not Found: The receiver is not a valid contract.
-
DVN Mismatch: All DVN providers must be the same on source and destination. See DVN Mismatch for more details.
-
Dead DVN: This configuration includes a Dead DVN. See Dead DVN for more details.
-
Block Confirmations Mismatch: Outbound confirmations must be ≥ inbound confirmations. See Block Confirmation Mismatch for more details.
Confirming
The system is currently validating transaction finality. This status may appear at any stage of the message lifecycle and typically represents a transitional state before progressing to the next step.
Malformed Command
The command is malformed. The status is only applied to lzRead message. To debug, see Debugging Malformed or Unresolvable Commands for more details.
Unresolvable Command
The command is unresolvable. This status is only applied to lzRead message. To debug, see Debugging Malformed or Unresolvable Commands for more details.
For Malformed Command and Unresolvable Command, an OApp must call skip()
to unblock the message pathway. If skip()
is not invoked, subsequent messages will not be delivered. See Skipping Nonce for more details.
To troubleshoot common errors in lzRead
messages, See debugging for more details.
SIMULATION_REVERTED
This status can be found in the LayerZero Scan API as a sub status inside the destination
section, indicating the lzReceive
or lzCompose
has failed on the destination chain.
General Debugging Steps
If the message was not sent successfully
Quick triage
- Confirm the transaction:
- Did the source chain transaction finalize? (Check explorer receipt status and logs.)
- Look for packet emission
- Verify whether the expected LayerZero “PacketSent” event is emitted on the source chain.
- Capture context:
- Source and destiantion chain
- OApp addresses
- send params
- DVN and Exeutor Configs
Identify the revert / error trace
Run a trace (Foundry/Tenderly/Trace on explorer) and map to the failing contract and error codes.
Common errors (causes & fixes):
Please set your OApp's DVNs and/or Executor
- Cause: This error occurs during
getFee
, indicating your OApp configuration is missing the required DVN and/or Executor settings. - Fix: Set valid DVNs and/or executor in the OApp configs.
InsufficientFee()
- Cause:
required.nativeFee
>suppliedNativeFee
orrequired.lzTokenFee
>suppliedLzTokenFee
; ormsg.value
lower than the quoted amount. - Fix: Call the quote function first, pass the exact fee, and forward enough msg.value.
NativeAmountExceedsCap()
- Cause: Requested native drop on destination exceeds the configured native drop cap.
- Fix: Reduce requested airdrop amount in options or raise the cap in the Executor/destination config (owner action).
InvalidWorkerOptions()
- Cause: Worker options is malformed.
- Fix: Rebuild options via the Options Builders.
Unauthorized()
- Cause: This error normally occurs at the wiring step. The call is not made by the OApp or an approved delegate.
- Fix: Use the permissioned wallet to sign the transactions.
Unsorted()
- Cause: DVNs array contains duplicates or is not strictly sorted.
- Fix: Deduplicate and sort DVN addresses deterministically before passing; keep canonical order in code.
UnsupportedEid()
- Cause: The pathway is not connected.
- Fix: Use the correct destination EID, verify chain mapping, and contact the support team if a pathway is not wired.
Configuration & connectivity checklist
- Delegate & Ownership
- Verify the owner and delegate address
- Understand their respective permissions
- Peers:
- peers are set on both source and destination chain
- addresses & EIDs match, and in correct format
- Message Libraries:
sendLibrary
andreceiveLibrary
are set to expected addresses/versions.
- DVNs:
- DVN provider set(s) exist
- Identical provider(s) on source and destination
- Contain no LZ Dead DVNs unless intended
- Executor:
- Executor address is set as intended
- Message size doesn't exceed Executor limit
- Native drop amount doesn't exceed cap
- Message Exeuction Options:
- Ensure enforcedOptions and/or extraOptions is present;
- Profiling destination gas for lzReceive and/or lzCompose to determine the gas units applied in the message execution options
- Connected pathways:
- Confirm whether a pathway is fully connected
If these pass but the send
still fails, simulate the send with the same params and use the error trace to narrow the root cause.
If the message was sent
Now the message is visible on LayerZero Scan. Use Scan to locate the message and walk the lifecycle.
-
Get transaction hash on the source chain
-
Start with Message Status on Scan
- Statuses map directly to lifecycle stages and tell you where to focus first:
-
Identify which stage the message is at (decision tree)
-
No DVN confirmations yet?
- Check source confirmations vs. threshold; verify DVN set correctly.
-
DVNs verified, but not committed?
- Check Executor config and contact support team.
-
Committed, but not Executed?
- If it is
orderedExecution
, inspect the first unverified prior message. - Inspect revert transaction and revert reason
- Fix root cause
- Retry the message
- If it is
-
lzReceive
succeeded butlzCompose
failed?- Inspect
lzcompose
revert reason - retry
lzCompose
.
- Inspect
-
Retrieve Retry Parameters via LayerZero Scan API
Use the LayerZero Scan API to fetch a message bundle by source tx hash and inspect the destination execution. If execution failed, the destination section includes failedTx, which typically points to an Executor alert call (e.g., lzReceiveAlert
or lzComposeAlert
). The alert transaction’s call data contains the parameters required to retry lzReceive
or lzCompose
.
Endpoint
Base URL: https://scan.layerzero-api.com/v1
Method: GET
/messages/tx/{tx}
tx
: source-chain transaction hash (hex string)
Response Shape
Each response returns a data
array of message objects:
- pathway: source/destination networks and EIDs, sender/receiver address, application information
- source: transaction details and status on source chain
- verification: DVN verification transactions and the committer/sealer transaction for committing verifications
- destination: execution status on the target chain (including failed txs)
- config: ULN configurations (Confirmations, DVNs, Executor) in effect for this pathway
- status: overall message status
- guid, created timestamps and updated timestamp
In the API response, look for failedTx
for the transactions that contains the eror for lzReceive
message. Examine the revertReason
in the destination section to identify the root cause.
API Response Example in the destination section:
"destination": {
"nativeDrop": {
"status": "N/A"
},
"lzCompose": {
"status": "N/A"
},
"failedTx": [
{
"txHash": "0xa6cf8347a8679866955fbf83175ccc3191f592c27865e89ce69bf99d71542b53",
"txError": "CouldNotParseError(string) 0x",
"blockHash": "0xa3130ed1fbb60b45bd99638a07f415892118442e72d5be6eda58214a6a41c610",
"blockNumber": 23266956,
"revertReason": "0x"
}
],
"status": "SIMULATION_REVERTED"
}
How to get retry parameters
- Call
GET /messages/tx/{tx}
with the source tx hash. Indata[0].destination.failedTx
, take thetxHash
(usually anlzReceiveAlert
orlzComposeAlert
transaction that the executor called to signal the failure). - Fetch that destination transaction and inspect.
- If revertReason is empty (0x), it commonly indicates out-of-gas or a contract-level revert without a reason string.
- For custom error, decode the selector using 4byte directory.
- Function selector & args of the alert call input; it embeds everything needed to re-invoke lzReceive/lzCompose
- Alternatively, all the information can also be retrieved directly from the scan API.
Skip/Clear/Burn/Nilify
skip
: Called by the receiver to skip verification and delivery of a nonce.
clear
: Called by the receiver to skip a nonce that has been verified.
nilify
: Called by the receiver to temporarily invalidate a nonce. nilify
can be used to proactively invalidate maliciously generated packets from compromised DVNs. Message can be re-executed.
burn
: Called by the receiver to delete and skip a nonce. burn
can be used if a faulty Security Stack commits an invalid hash to the endpoint, or if an OApp needs to clear a nilified nonce. Message can not be re-executed.
See: Skip/Clear/Burn/Nilify on EVM and Skip/Clear/Burn/Nilify on Solana for full semantics and usage.