nautechsystems/nautilus_trader · error · anyhow::Error

missing raw broker client order ID payload

Error message

missing raw broker client order ID payload

What it means

Decoding a broker-prefixed client order ID whose signal byte marks a raw (unpacked) payload: everything after the signal byte should be the verbatim ClientOrderId, but the payload is empty, leaving nothing to reconstruct.

Source

Thrown at crates/adapters/binance/src/common/encoder.rs:257

        SIGNAL_O_HYPHENS | SIGNAL_O_NO_HYPHENS => {
            anyhow::ensure!(
                data.len() == O_FORMAT_B62_LEN,
                "invalid O-format broker client order ID payload length"
            );
            let packed = decode_base62(data).context("invalid O-format broker client order ID")?;
            Ok(unpack_o_format(packed, signal == SIGNAL_O_HYPHENS))
        }
        SIGNAL_UUID_HYPHENS | SIGNAL_UUID_NO_HYPHENS => {
            anyhow::ensure!(
                data.len() == UUID_B62_LEN,
                "invalid UUID broker client order ID payload length"
            );
            let value = decode_base62(data).context("invalid UUID broker client order ID")?;
            Ok(format_uuid(value, signal == SIGNAL_UUID_HYPHENS))
        }
        SIGNAL_RAW => {
            let raw = std::str::from_utf8(data).context("invalid raw broker client order ID")?;
            anyhow::ensure!(
                !raw.is_empty(),
                "missing raw broker client order ID payload"
            );
            Ok(raw.to_string())
        }
        _ => anyhow::bail!(
            "unknown broker client order ID signal byte '{}'",
            signal as char
        ),
    }
}

fn build_encoded(prefix: &str, signal: u8, b62: &[u8]) -> String {
    let mut result = String::with_capacity(prefix.len() + 1 + b62.len());
    result.push_str(prefix);
    result.push(signal as char);
    // base62 output is always valid ASCII
    result.push_str(std::str::from_utf8(b62).expect("base62 is valid UTF-8"));

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Use a longer, distinctive BinanceBrokerId
  2. Regenerate the order IDs with the current adapter encoder rather than reconstructing them by hand
  3. Validate stored/captured IDs for length before decoding

Example fix

# before
broker_id = "B"            # prefix+signal collisions trivial

# after
broker_id = "NAUTILUS7F"   # distinctive, collisions virtually impossible
Defensive patterns

Strategy: validation

Validate before calling

fn raw_payload_present(id: &str, prefix: &str) -> bool {
    id.strip_prefix(prefix).is_none_or(|p| p.len() >= 2) // signal + payload
}

Type guard

fn broker_id_decodable(id: &str, prefix: &str) -> bool {
    id.strip_prefix(prefix).is_none_or(|p| !p.is_empty() && p.len() > 1)
}

Try / catch

let decoded = decode_broker_id_checked(encoded, broker_id)
    .unwrap_or_else(|e| {
        log::warn!("raw payload missing for '{encoded}': {e}");
        encoded.to_string()
    });

Prevention

When it happens

Trigger: A clientOrderId consisting exactly of broker prefix + raw-signal character with no following bytes - typically a truncated ID or a foreign ID that collides with a short broker prefix.

Common situations: Short/generic broker_id values colliding with exchange-generated IDs; IDs cut down to the length limit; malformed test fixtures.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/300c34855275732d. Report an issue: GitHub.