headroomlabs-ai/headroom · error · ParseError::ImplausiblePreludeLengths
bedrock_eventstream_parse_failed
bedrock_eventstream_parse_failed
Error message
implausible prelude lengths: total_length={total_length}, headers_length={headers_length} What it means
MemoryHandler initialization rejected the configured backend string (memory_handler.py:443). Only 'local' and 'qdrant-neo4j' are recognized; anything else — 'qdrant', 'sqlite', 'chroma', 'none', empty-but-set, or a typo — raises ValueError during initialize(), which surfaces at startup or first memory use.
Source
Thrown at crates/headroom-proxy/src/bedrock/eventstream.rs:149
self.headers.get(":event-type").and_then(|v| v.as_str())
}
/// Read the `:message-type` header as a string. Bedrock uses
/// `event` (data frames) vs `exception` (synchronous errors).
pub fn message_type(&self) -> Option<&str> {
self.headers.get(":message-type").and_then(|v| v.as_str())
}
}
/// Errors surfaced by the parser. Per project rules these are
/// structured (not `String`) so the handler can dispatch on them.
#[derive(Debug, Error)]
pub enum ParseError {
/// `total_length` smaller than the minimum a well-formed message
/// requires (12-byte prelude, headers block, 4-byte trailing CRC).
/// Wire-format violation; AWS would never emit this. Surfaced
/// loudly so the handler can 5xx and log it.
#[error(
"implausible prelude lengths: total_length={total_length}, headers_length={headers_length}"
)]
ImplausiblePreludeLengths {
total_length: u32,
headers_length: u32,
},
/// `total_length` exceeds the configured `max_message_bytes` cap.
/// The cap defaults to 32 MiB; configurable via
/// [`EventStreamParser::with_max_message_bytes`].
#[error("message too large: total_length={total_length} cap={cap}")]
MessageTooLarge { total_length: u32, cap: usize },
/// CRC32 of the first 8 bytes of the prelude did not match the
/// 9th-12th bytes. Indicates corruption (in-flight bit-flip,
/// truncated chunk, or — if persistent — wire-format version skew).
#[error("prelude CRC mismatch: expected={expected:#010x} got={got:#010x}")]
PreludeCrcMismatch { expected: u32, got: u32 },
/// CRC32 of the entire message body did not match the trailing
/// 4-byte CRC. Indicates the message bytes were corrupted inView on GitHub (pinned to 322425c43b)
Solutions
- Set the backend to 'local' (embedded) or 'qdrant-neo4j' (external vector+graph stack)
- Check for typos/case in your config file, CLI flag, or environment variable before the memory handler initializes
- If you meant to disable memory, use the memory disable option rather than an invalid backend string
Example fix
# before (config) memory_backend = "qdrant" # after memory_backend = "qdrant-neo4j"
Defensive patterns
Strategy: validation
Validate before calling
VALID_BACKENDS = {"local", "qdrant-neo4j"}
if config.get("backend") not in VALID_BACKENDS:
raise SystemExit(f"memory backend must be one of {sorted(VALID_BACKENDS)}, got {config.get('backend')!r}") Type guard
from typing import Literal, TypeGuard
MemoryBackend = Literal["local", "qdrant-neo4j"]
def is_valid_backend(v: str) -> TypeGuard[MemoryBackend]:
return v in ("local", "qdrant-neo4j") Prevention
- Re-check backend names after upgrading headroom
- Validate config files in CI before deploy
When it happens
Trigger: Setting memory_backend/memory-backend config or CLI flag to a nonexistent backend name; renaming between versions (e.g. older 'qdrant' vs current 'qdrant-neo4j'); env-derived values with whitespace or wrong casing.
Common situations: Upgrading headroom and reusing an old config file; copy-pasting backend names from docs of a different version; disabling intent expressed as 'none' or 'off' which is not a valid backend.
Related errors
- bedrock_eventstream_crc_mismatch
- max_size must be at least 1, got {max_size}
- save_path must be provided when auto_save is True
- Unknown backend: {self._backend_type}
- recommendations IO error at {path}: {source}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/49f8e91c84fc58ca.
Report an issue: GitHub.