Skip to content

Sealevel/Solana relayer produces confusing error when signer key is missing #7582

@danwt

Description

@danwt

Summary

When running the relayer for Sealevel/Solana chains without a signer key configured (e.g., missing --chains.<chain>.signer.key flag), the error message is extremely misleading:

ERROR hyperlane_sealevel::mailbox: error: No return data from InboxGetRecipientIsm instruction
    at chains/hyperlane-sealevel/src/mailbox.rs:447
    in hyperlane_sealevel::mailbox::recipient_ism with recipient: 0x...
    in relayer::msg::message_processor::prepare_classic_task with domain: solanatestnet
WARN relayer::msg::pending_message: Repreparing message: Error fetching ISM address, error: ContractError(StringError("No return data from InboxGetRecipientIsm instruction"))

This error gives no indication that the actual problem is a missing signer key. We spent many hours debugging this before realizing the root cause was simply a missing --chains.solanatestnet.signer.key flag.

Root Cause

The issue is in the code flow:

  1. build_mailbox() in hyperlane-base/src/settings/chains.rs calls sealevel_signer() which returns Option<Keypair>
  2. The optional keypair is passed to SealevelMailbox::new() as Option<SealevelKeypair>
  3. The mailbox is created successfully with payer: None
  4. Later, when relaying a message, methods like get_account_metas(), simulate_instruction(), and get_recipient_ism() are called
  5. These methods require a payer, and when None, they fail with ChainCommunicationError::SignerUnavailable
  6. However, the simulation failure cascades into "No return data from InboxGetRecipientIsm instruction" which is completely opaque

Expected Behavior

The relayer should fail immediately at startup with a clear error message like:

Error: Sealevel chain 'solanatestnet' requires a signer key. 
Please configure: --chains.solanatestnet.signer.key <hex_private_key> or --defaultSigner.key <hex_private_key>

Proposed Fix

Add validation in build_mailbox() to require a signer for Sealevel chains:

ChainConnectionConf::Sealevel(conf) => {
    let keypair = self.sealevel_signer().await.context(ctx)?;
    
    // Sealevel requires a signer for mailbox operations
    let keypair = keypair.ok_or_else(|| {
        eyre!(
            "Sealevel chain '{}' requires a signer key. \
             Please configure: --chains.{}.signer.key <hex_private_key> \
             or --defaultSigner.key <hex_private_key>",
            self.domain.name(),
            self.domain.name()
        )
    })?;
    
    // ... rest of mailbox construction
}

This is consistent with how Fuel chains handle the signer requirement (see fuel_signer() which explicitly requires a signer and fails fast).

Impact

  • Severity: Medium (causes significant debugging time waste)
  • Affected chains: All Sealevel/Solana chains
  • Affected operations: Relaying messages (mailbox.process)

Environment

  • Rust agent
  • Sealevel/Solana chains

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions