vectordotdev/vector · error
early buffer was already consumed
Error message
early buffer was already consumed
What it means
`consume_early_buffer` takes the buffered early log events out of the static BUFFER via `.take()`. `take()` returns `None` if the buffer is already `None`, meaning the early buffer was consumed once before; the `expect` then panics. Per its doc comment, consuming twice is a caller-contract violation.
Solutions
- Ensure `stop_early_buffering` is called exactly once per process lifetime
- Guard the stop call with an idempotency check (e.g. a OnceLock/atomic flag)
- Use `consume_early_buffer`'s caller contract: check `should_process_tracing_event`/buffer state before consuming
- In tests, run each case in a separate process or serialize setup
Example fix
// before
stop_early_buffering();
stop_early_buffering(); // panics
// after
static STOPPED: OnceLock<()> = OnceLock::new();
if STOPPED.set(()).is_ok() {
stop_early_buffering();
} Defensive patterns
Strategy: validation
Validate before calling
// idempotent guard around consume
static CONSUMED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
if !CONSUMED.swap(true, std::sync::atomic::Ordering::SeqCst) {
stop_early_buffering();
} Prevention
- Call stop_early_buffering exactly once per process
- Use OnceLock/atomic flags for idempotency
- Serialize shutdown logic; avoid concurrent stop calls
- Isolate per-test tracing state
When it happens
Trigger: Calling `consume_early_buffer` (through `stop_early_buffering`) more than once — e.g. `stop_early_buffering` invoked twice, or after early buffering was already stopped and consumed.
Common situations: Duplicate shutdown/initialization sequences in tests that call `stop_early_buffering` per test without isolation; race conditions where two tasks both attempt to stop early buffering concurrently.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Couldn't acquire lock on internal logs buffer
- poisoned locks are dumb
- span must already exist!
- a record with a next ID must have an event count
- a valid HTTP/1 URI is valid as an HTTP URI
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/d1d0e292033e36d9.
Report an issue: GitHub.
Appendix: source
Thrown at src/trace.rs:193
/// Attempts to broadcast an event to subscribers.
///
/// If no subscribers are connected, this does nothing.
fn try_broadcast_event(log: LogEvent) {
if let Some(sender) = maybe_get_trace_sender() {
_ = sender.send(log);
}
}
/// Consumes the early buffered events.
///
/// # Panics
///
/// If the early buffered events have already been consumed, this function will panic.
fn consume_early_buffer() -> Vec<LogEvent> {
get_early_buffer()
.take()
.expect("early buffer was already consumed")
}
/// Gets or creates a trace sender for sending internal log events.
fn get_trace_sender() -> &'static broadcast::Sender<LogEvent> {
SENDER.get_or_init(|| broadcast::channel(99).0)
}
/// Attempts to get the trace sender for sending internal log events.
///
/// If the trace sender has not yet been created, `None` is returned.
fn maybe_get_trace_sender() -> Option<&'static broadcast::Sender<LogEvent>> {
SENDER.get()
}
/// Creates a trace receiver that receives internal log events.
///
/// This will create a trace sender if one did not already exist.
fn get_trace_receiver() -> broadcast::Receiver<LogEvent> {View on GitHub (pinned to bdb87aeaa4)