nautechsystems/nautilus_trader · error
Invalid stream field key: {stream_msg:?}
Error message
Invalid stream field key: {stream_msg:?} What it means
decode_bus_message iterates key/value pairs and each key must be a redis::Value::BulkString. If a key is some other variant (Int, Nil, simple status string, etc.) the decoder bails with this error rather than guessing the field name.
Source
Thrown at crates/infrastructure/src/redis/msgbus.rs:824
// Redis fields are unordered, and older streams may omit type or encoding headers
fn decode_bus_message(stream_msg: &redis::Value) -> anyhow::Result<BusMessage> {
let redis::Value::Array(fields) = stream_msg else {
anyhow::bail!("Invalid stream message format: {stream_msg:?}");
};
if fields.len() < 4 || fields.len() % 2 != 0 {
anyhow::bail!("Invalid stream message format: {stream_msg:?}");
}
let mut topic: Option<String> = None;
let mut type_name: Option<String> = None;
let mut typed_payload = false;
let mut encoding = SerializationEncoding::default();
let mut payload: Option<Bytes> = None;
for pair in fields.as_chunks::<2>().0 {
let redis::Value::BulkString(key) = &pair[0] else {
anyhow::bail!("Invalid stream field key: {stream_msg:?}");
};
match key.as_slice() {
b"topic" => {
let redis::Value::BulkString(bytes) = &pair[1] else {
anyhow::bail!("Invalid topic format: {stream_msg:?}");
};
topic = Some(
String::from_utf8(bytes.clone())
.map_err(|e| anyhow::anyhow!("Error parsing topic: {e}"))?,
);
}
b"type" => {
let redis::Value::BulkString(bytes) = &pair[1] else {
anyhow::bail!("Invalid type format: {stream_msg:?}");
};
type_name = Some(
String::from_utf8(bytes.clone())View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the entry ({stream_msg:?}) to see which key element has the unexpected variant
- Ensure test fixtures build entries as Value::BulkString keys paired with values
- Force RESP2 (or match server/client protocol versions) so replies use BulkString representations
- Bypass or fix any proxy that rewrites reply types
Defensive patterns
Strategy: type-guard
Type guard
fn has_bulkstring_keys(v: &redis::Value) -> bool {
matches!(v, redis::Value::Array(f) if f.len() % 2 == 0 &&
f.chunks(2).all(|c| matches!(c[0], redis::Value::BulkString(_))))
} Try / catch
match decode_bus_message(&entry) {
Err(e) if e.to_string().contains("Invalid stream field key") => {
log::warn!("non-string key in entry, skipping: {e}");
}
other => other?,
} Prevention
- Build test fixtures with Value::BulkString keys
- Pin RESP2/RESP3 negotiation consistently across client and server
- Skip proxy layers that transform reply value variants
When it happens
Trigger: A stream entry whose odd-indexed elements are not BulkStrings — normally impossible from real Redis XADD replies, so usually caused by mock/test data, a proxy rewriting replies, or misusing the decoder with fabricated values.
Common situations: Unit or integration fixtures constructing redis::Value entries incorrectly; RESP3 protocol negotiation with a client/server mismatch altering variant representations; custom middleware translating replies.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Invalid stream message format: {stream_msg:?}
- Invalid topic format: {stream_msg:?}
- Invalid type format: {stream_msg:?}
- Invalid payload kind format: {stream_msg:?}
- Invalid encoding format: {stream_msg:?}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/41b52cb5ff7e8d32.
Report an issue: GitHub.