nautechsystems/nautilus_trader · error · anyhow::Error

unknown broker client order ID signal byte '{}'

Error message

unknown broker client order ID signal byte '{}'

What it means

Decoding a broker-prefixed client order ID: the first byte after the prefix must be one of the known signal bytes (O-format, UUID, or raw variants). The observed byte matches none of them, so the payload format is unrecognizable.

Source

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

            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"));
    result
}

fn encode_base62<const N: usize>(mut value: u128) -> [u8; N] {
    let mut buf = [b'0'; N];
    for i in (0..N).rev() {

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Configure a longer, distinctive broker_id so unrelated IDs never share the prefix
  2. Upgrade the reading side to the adapter version that produced the IDs (new signal bytes appear with format extensions)
  3. If the ID genuinely is not yours, treat it as an opaque external ID instead of decoding

Example fix

// before
match decode_broker_id_checked(encoded, broker_id) {
    Ok(cid) => cid,
    Err(e) => return Err(e),
}

// after: unknown signal -> opaque raw ID, keep processing updates
let cid = decode_broker_id_checked(encoded, broker_id)
    .unwrap_or_else(|_| encoded.to_string());
Defensive patterns

Strategy: fallback

Validate before calling

const KNOWN_SIGNALS: &[u8] = b"oOuUw"; // however the adapter spells them - mirror encoder constants
fn has_known_signal(id: &str, prefix: &str) -> bool {
    id.strip_prefix(prefix)
        .and_then(|p| p.as_bytes().first())
        .is_none_or(|b| KNOWN_SIGNALS.contains(b))
}

Type guard

fn is_adapter_generated_id(id: &str, prefix: &str) -> bool {
    id.strip_prefix(prefix)
        .and_then(|p| p.as_bytes().first())
        .is_some_and(|b| KNOWN_SIGNALS.contains(b))
}

Try / catch

let cid = match decode_broker_id_checked(encoded, broker_id) {
    Ok(decoded) => ClientOrderId::new_checked(decoded)?,
    Err(_) => {
        // unknown signal -> assume foreign exchange-generated ID, use verbatim
        ClientOrderId::new(encoded)
    }
};

Prevention

When it happens

Trigger: Any clientOrderId that starts with the broker prefix but continues with a byte the encoder never emits - foreign exchange-generated IDs, user-supplied IDs, or a payload format introduced in a newer adapter version than the decoder.

Common situations: Broker prefix configured so short (1-2 chars) that Binance's own appended IDs (e.g. autoclose/conditional suffixes) begin with it; reading orders created by a newer adapter; hand-built IDs.

Related errors


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