vectordotdev/vector · error · LinesCodecError::Io
InvalidData
InvalidData
Error message
Unable to decode message len as number
What it means
The octet-counting framer (RFC 6587 style, `"<digits> <payload>"`) parses the ASCII digits before the space as the message length. If those bytes are not valid UTF-8 digits that parse as a `usize`, it advances past them (so decoding can make progress) and returns an `InvalidData` io error wrapped in `LinesCodecError::Io`. The framing method is selected via `framing.method = "octet_counting"` on the source's decoding config.
Source
Thrown at lib/codecs/src/decoding/framing/octet_counting.rs:150
(State::NotDiscarding, _, Some(space_pos)) if space_pos < self.other.max_length() => {
// Everything looks good.
//
// We aren't discarding, we have a space that is not beyond our
// maximum length. Attempt to parse the bytes as a number which
// will hopefully give us a sensible length for our message.
let len: usize = match std::str::from_utf8(&src[..space_pos])
.map_err(|_| ())
.and_then(|num| num.parse().map_err(|_| ()))
{
Ok(len) => len,
Err(_) => {
// It was not a sensible number.
//
// Advance the buffer past the erroneous bytes to
// prevent us getting stuck in an infinite loop.
src.advance(space_pos + 1);
self.octet_decoding = None;
return Err(LinesCodecError::Io(io::Error::new(
io::ErrorKind::InvalidData,
"Unable to decode message len as number",
)));
}
};
let from = space_pos + 1;
let to = from + len;
if len > self.other.max_length() {
// The length is greater than we want.
//
// We need to discard the entire message.
self.octet_decoding = Some(State::Discarding(len));
src.advance(space_pos + 1);
Ok(None)
} else if let Some(msg) = src.get(from..to) {View on GitHub (pinned to 3708c39b12)
Solutions
- Align framing on both ends: either enable octet counting on the sender (rsyslog `omfwd` with `TCP_Framing="octet-counted"`) or switch Vector's `framing.method` to `newline_delimited`/`character_delimited`.
- If input is genuinely mixed, split it across two sources on separate ports with the correct framing each.
- Capture a few payloads to confirm whether the `<len> ` prefix is present at all before changing config.
Example fix
# before
decoding:
framing:
method: octet_counting
# sender emits plain newline-delimited syslog
# after
decoding:
framing:
method: newline_delimited Defensive patterns
Strategy: try-catch
Try / catch
match decoder.decode(&mut src).await {
Ok(Some(frame)) => { /* handle */ }
Ok(None) => {}
Err(LinesCodecError::Io(ref e)) if e.kind() == io::ErrorKind::InvalidData => {
// framing prefix was not a number: bytes were skipped, keep reading
warn!(error = %e, "skipping non-octet-counted frame");
}
Err(e) => return Err(e.into()),
} Prevention
- Lock framing choice in the sender and Vector config to the same mode; document it next to the port.
- For syslog senders, prefer octet counting end-to-end (rsyslog `TCP_Framing="octet-counted"`).
- The decoder advances past bad bytes, so logging at warn and continuing is safe.
When it happens
Trigger: Data arriving on a port/framing set to octet_counting that does not start with `<len> ` — e.g. syslog sent without the octet-counting prefix (newline-delimited BSD syslog), a leading newline/space, a negative or oversized number, or a length containing separators like commas.
Common situations: Sender set to `syslog.frame_type = "non_transparent_framing"` (newline) while Vector expects octet counting; sending plain text lines to an octet_counting input; non-ASCII bytes at datagram start (binary protocols on a reused port).
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid chunk header with less than 10 bytes: 0x{header:0x}
- valid message key
- invalid timestamp
- keys/counts were unexpectedly mismatched
- Received chunk with message id {message_id} and sequence num
AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20).
Data as JSON: /api/errors/245c9f0bee8f4d10.
Report an issue: GitHub.