vectordotdev/vector · error · ChunkedGelfDecoderError::BufferedPayloadLimitReached

Buffered payload limit of

Error message

Buffered payload limit of {limit} bytes reached while processing chunk with message id {message_id} and sequence number {sequence_number}. Discarding all buffered chunks of that message.

What it means

Beyond counting pending messages, the chunked GELF decoder bounds total buffered bytes per incomplete message via `max_buffered_payload`. Before appending a chunk, it checks `pending.buffered_payload + chunk_len <= max_buffered_payload` (saturating add) and, if exceeded, fails with this error and discards all buffered chunks of that message. This prevents a single oversized or repeatedly re-sent message id from exhausting memory.

Solutions

  1. Increase `max_buffered_payload` in the source configuration to accommodate your largest legitimate chunked GELF messages.
  2. Split oversized messages at the sender or send them via a non-chunked transport (TCP GELF) instead.
  3. Check for packet loss/duplication causing the same message id to be retransmitted and inflate the buffer.
  4. Note the affected message's buffered chunks are discarded on this error — retransmission must come from the sender.

Example fix

# before
sources:
  gelf:
    decoding:
      chunked_gelf:
        max_buffered_payload: 65535

# after
sources:
  gelf:
    decoding:
      chunked_gelf:
        max_buffered_payload: 1048576
Defensive patterns

Strategy: fallback

Validate before calling

// ensure the largest message fits the buffer
let max_msg_bytes = total_chunks_max * chunk_size;
assert!(max_msg_bytes <= max_buffered_payload,
        "max_buffered_payload must be at least {max_msg_bytes} bytes");

Try / catch

match framing.decode(&datagram) {
    Err(e) if e.to_string().contains("Buffered payload limit") => {
        // chunks of this message were discarded; rely on sender retransmission
        warn!("oversized/duplicated GELF message dropped: {e}");
        metrics.incr("gelf_payload_limit_hit");
    }
    other => { /* handle normally */ }
}

Prevention

When it happens

Trigger: decode_chunk receives a chunk for an existing pending message where adding the chunk's length to `pending.buffered_payload` (saturating) would exceed `self.max_buffered_payload` — either very large accumulated messages or the same message id being re-sent repeatedly due to loss.

Common situations: A sender emitting GELF messages larger than the configured buffer allows; severe packet loss causing duplicate re-sends of the same message id that inflate the buffered payload; a too-low `max_buffered_payload` setting relative to legitimate message sizes.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/f83b6296b7faec99. Report an issue: GitHub.

Appendix: source

Thrown at lib/codecs/src/decoding/framing/chunked_gelf.rs:486

        // applies only on insert, since rejecting chunks of pending messages would stall them.
        if is_new_message {
            if chunk_len > self.max_length {
                return Err(ChunkedGelfDecoderError::MaxLengthExceed {
                    message_id,
                    sequence_number,
                    length: chunk_len,
                    max_length: self.max_length,
                });
            }
            ensure!(
                pending.messages.len() < self.pending_messages_limit,
                PendingMessagesLimitReachedSnafu {
                    message_id,
                    sequence_number,
                    pending_messages_limit: self.pending_messages_limit
                }
            );
            ensure!(
                pending.buffered_payload.saturating_add(chunk_len) <= self.max_buffered_payload,
                BufferedPayloadLimitReachedSnafu {
                    message_id,
                    sequence_number,
                    limit: self.max_buffered_payload,
                }
            );
        }

        if is_new_message {
            let state = Arc::clone(&self.state);
            let timeout = self.timeout;
            let timeout_handle = tokio::spawn(async move {
                tokio::time::sleep(timeout).await;
                let timeout_task_id = tokio::task::id();
                let mut pending = state.lock().expect("poisoned lock");
                let owns_message = pending
                    .messages

View on GitHub (pinned to bdb87aeaa4)