linera-io/linera-protocol · warning

invalid hex

Error message

invalid hex

What it means

The is_deposit_processed GraphQL query handler hex-decodes the submitted hash string and expects success. The panic means the query argument was not valid hex — odd-length string, non-hex characters, or a 0x-prefixed string with junk after the prefix (the optional 0x is stripped, everything else must decode). A panic in a query handler surfaces as a GraphQL error response rather than a boolean result.

Source

Thrown at linera-bridge/contracts/evm-bridge/src/service.rs:195

    /// The ERC-20 token address on the source EVM chain (hex-encoded).
    async fn token_address(&self) -> String {
        let params: BridgeParameters = self.runtime.application_parameters();
        format!("0x{}", hex::encode(params.token_address))
    }

    /// The configured EVM JSON-RPC endpoint, or empty if finality verification
    /// is disabled.
    async fn rpc_endpoint(&self) -> String {
        self.state.rpc_endpoint.get().clone()
    }

    /// Whether a deposit with the given hash has been processed.
    ///
    /// The hash is the hex-encoded keccak-256 of the deposit key
    /// (see [`evm_bridge::DepositKey::hash`]).
    async fn is_deposit_processed(&self, hash: String) -> bool {
        let bytes: [u8; 32] = hex::decode(hash.strip_prefix("0x").unwrap_or(&hash))
            .expect("invalid hex")
            .try_into()
            .expect("hash must be 32 bytes");
        self.state
            .processed_deposits
            .contains(&bytes)
            .await
            .expect("failed to check processed deposits")
    }

    /// Verifies that the given EVM block hash is finalized on the source chain.
    ///
    /// Makes the EVM JSON-RPC calls in the service runtime so that the contract
    /// sees a single deterministic oracle response (the boolean result) instead
    /// of multiple raw HTTP responses with non-deterministic headers.
    async fn is_block_hash_finalized(&self, block_hash: String) -> bool {
        let bytes: [u8; 32] = hex::decode(block_hash.strip_prefix("0x").unwrap_or(&block_hash))
            .expect("invalid hex")
            .try_into()

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Send the hash exactly as hex::encode produced it: 0x + 64 lowercase hex chars
  2. Validate client-side with a regex like /^0x[0-9a-fA-F]{64}$/ before issuing the query
  3. If you control the service, replace the expect with a GraphQL error (Result-based handler) for a friendlier message
  4. Check for stray whitespace, quotes, or truncation in the query template

Example fix

# before
query { isDepositProcessed(hash: "0x9f2b1c" ) }  # truncated / odd-length hex -> GraphQL error

# after
# Always send keccak-256 output verbatim: 0x + 64 hex chars
query { isDepositProcessed(hash: "0x3fa85164e8b7c0c2d4a6b9e0f5d2c8a1b7e6d4f3c2a1908e7d6c5b4a3928f1e0d") }
Defensive patterns

Strategy: validation

Validate before calling

// Validate before querying:
fn is_query_hash(s: &str) -> bool {
    let h = s.strip_prefix("0x").unwrap_or(s);
    h.len() == 64 && h.chars().all(|c| c.is_ascii_hexdigit())
}
assert!(is_query_hash(&hash));
let processed = service.is_deposit_processed(hash).await;

Type guard

fn is_deposit_hash_query_arg(s: &str) -> bool {
    let h = s.strip_prefix("0x").unwrap_or(s);
    h.len() % 2 == 0 && h.len() == 64 && h.chars().all(|c| c.is_ascii_hexdigit())
}

Try / catch

// GraphQL surfaces the panic as an errors[] entry; match on the message:
match resp.errors.first() {
    Some(e) if e.message.contains("invalid hex") => fix_client_encoding(),
    _ => use_result(resp.data),
}

Prevention

When it happens

Trigger: Querying isDepositProcessed(hash: ...) with a base64 string, an odd-length hex string, a hash with a '0X' uppercase prefix containing invalid chars, or an empty string after prefix stripping; passing the raw DepositKey bytes instead of their hex encoding.

Common situations: Frontends that render hashes with mixed case or ellipsis truncation before querying; scripts piping arbitrary identifiers into the query; confusion between keccak hex output and multihash/base58 encodings.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/c733d09dcdd7069b. Report an issue: GitHub.