nautechsystems/nautilus_trader · error

Expected array payload: {e}

Error message

Expected array payload: {e}

What it means

parse_message_vec deserializes a serde_json::Value into Vec<T> before applying the per-message parser. The error 'Expected array payload' is raised when the JSON value is not an array (or the elements don't match T), e.g. an object or error payload from OKX.

Source

Thrown at crates/adapters/okx/src/common/parse.rs:1364

/// Reduces code duplication by providing a common pattern for deserializing JSON arrays,
/// parsing each message, and wrapping results in Nautilus Data enum variants.
///
/// # Errors
///
/// Returns an error if the payload is not an array or if individual messages
/// cannot be parsed.
pub fn parse_message_vec<T, R, F, W>(
    data: serde_json::Value,
    parser: F,
    wrapper: W,
) -> anyhow::Result<Vec<Data>>
where
    T: DeserializeOwned,
    F: Fn(&T) -> anyhow::Result<R>,
    W: Fn(R) -> Data,
{
    let messages: Vec<T> =
        serde_json::from_value(data).map_err(|e| anyhow::anyhow!("Expected array payload: {e}"))?;

    let mut results = Vec::with_capacity(messages.len());

    for message in &messages {
        let parsed = parser(message)?;
        results.push(wrapper(parsed));
    }

    Ok(results)
}

/// Converts a Nautilus bar specification into the matching OKX candle channel.
///
/// # Errors
///
/// Returns an error if the provided bar specification does not have a matching
/// OKX websocket channel.
pub fn bar_spec_as_okx_channel(bar_spec: BarSpecification) -> anyhow::Result<OKXWsChannel> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the raw payload and confirm whether it is an OKX error/event message rather than a data array
  2. Handle OKX 'event' messages (subscribe/error) before dispatching to array parsers
  3. Check that the subscribed channel produces the expected array-of-records shape
  4. Verify adapter/OKX API version alignment for the channel's schema

Example fix

// before
let reports = parse_message_vec(&data, parse_ticker_msg, Data::Ticker)?;
// after
if !data.is_array() {
    tracing::warn!(?data, "Non-array payload; skipping");
    return Ok(Vec::new());
}
let reports = parse_message_vec(&data, parse_ticker_msg, Data::Ticker)?;
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_array_payload(v: &serde_json::Value) -> bool { v.is_array() }
if !is_array_payload(&data) { /* handle OKX event/error message first */ }

Type guard

fn as_msg_array(v: &serde_json::Value) -> Option<&Vec<serde_json::Value>> { v.as_array() }

Try / catch

match parse_message_vec(&data, parse_ticker_msg, Data::Ticker) {
    Ok(data_vec) => handle(data_vec),
    Err(e) => tracing::debug!("non-array websocket payload ignored: {e:#}"),
}

Prevention

When it happens

Trigger: Calling the WebSocket-specific message parsers (ticker, quote, trade, mark/index price msg vec) with a data payload that is a JSON object or string rather than an array of channel messages.

Common situations: OKX pushes an error event or subscribe confirmation instead of the data array; channel data format changes; wiring the wrong event payload into parse_message_vec.

Related errors


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