nautechsystems/nautilus_trader · error

Failed to parse {method} result '{hex_string}': {e}

Error message

Failed to parse {method} result '{hex_string}': {e}

What it means

Thrown by `parse_hex_quantity_result` when the JSON-RPC result string exists but is not a valid hex quantity parseable by `u128::from_str_radix` after stripping the `0x` prefix. This protects callers from silently treating malformed node data as a numeric quantity.

Source

Thrown at crates/adapters/blockchain/src/rpc/http.rs:1031

/// Classifies a broadcast transport failure: a timeout after sending reconciles through the
/// persisted record. Any other transport failure is treated as ambiguous-after-send too, even
/// though some (for example connection refused) may never have reached the node; the HTTP
/// client does not expose the connect-phase distinction, so the conservative outcome never
/// risks a rebroadcast. Sanitized to never carry the endpoint URL or request payload.
fn classify_broadcast_transport_error(error: &HttpClientError) -> BroadcastError {
    match error {
        HttpClientError::TimeoutError(_) => BroadcastError::TimeoutAfterSend,
        _ => BroadcastError::Failed("transport error".to_string()),
    }
}

/// Parses a required hex-quantity JSON-RPC result, erroring on a missing result.
fn parse_hex_quantity_result(method: &str, result: Option<String>) -> anyhow::Result<u128> {
    let hex_string = result.ok_or_else(|| anyhow::anyhow!("{method} returned no result"))?;
    let stripped = hex_string.strip_prefix("0x").unwrap_or(&hex_string);
    u128::from_str_radix(stripped, 16)
        .map_err(|e| anyhow::anyhow!("Failed to parse {method} result '{hex_string}': {e}"))
}

#[cfg(test)]
pub(crate) mod tests {
    use alloy::primitives::{address, b256};
    use rstest::rstest;

    use super::*;

    #[rstest]
    fn rpc_response_preview_truncates_on_utf8_boundary() {
        let raw = format!("{}é", "a".repeat(499));

        let preview = rpc_response_preview(&raw);

        assert_eq!(
            preview,
            format!("{}... (truncated, 501 bytes total)", "a".repeat(499))

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log/inspect the raw result string to see what the node actually returned.
  2. Point the client at a spec-compliant EVM node; verify with curl that eth_chainId returns e.g. "0x1".
  3. If values may exceed u128 in your chain, this adapter's u128 bound is exceeded — use a node whose quantities fit or handle the value upstream.
  4. Add a fallback endpoint that returns well-formed hex quantities.

Example fix

// before
// mock server returns {"result": "1"} (decimal) -> parse fails
// after
// mock server returns {"result": "0x1"} (hex quantity)
Defensive patterns

Strategy: validation

Validate before calling

fn is_hex_quantity(s: &str) -> bool {
    let body = s.strip_prefix("0x").unwrap_or(s);
    !body.is_empty() && body.chars().all(|c| c.is_ascii_hexdigit())
        && body.parse::<u128>().map_err(|_| u128::from_str_radix(body, 16).is_ok())
        && u128::from_str_radix(body, 16).is_ok()
}
// validate the raw result string before passing it downstream

Type guard

fn as_hex_quantity(v: &serde_json::Value) -> Option<u128> {
    let s = v.as_str()?;
    let body = s.strip_prefix("0x").unwrap_or(s);
    u128::from_str_radix(body, 16).ok()
}

Try / catch

match client.chain_id() {
    Err(e) if e.to_string().starts_with("Failed to parse eth_chainId result") => {
        tracing::error!("non-compliant node returned a non-hex quantity; audit endpoint");
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: A method like eth_chainId/eth_getTransactionCount/eth_estimateGas/eth_maxPriorityFeePerGas returning a non-hex-quantity string — e.g. a decimal string, a 0x-prefixed value exceeding u128, an empty string, or garbage text — reaching `u128::from_str_radix`.

Common situations: Non-EVM or misbehaving mock/proxy servers that return decimal numbers as strings; quantity fields exceeding u128 range (rare but possible for some values); provider returning error text inside `result` instead of `error`.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/b7b90fe42e666ebf. Report an issue: GitHub.