influxdata/influxdb · error
can only accept once
Error message
can only accept once
What it means
buffer_channel::channel() creates a one-shot hand-off for a linear_buffer::Slice between the object store and the in-memory cache. BufferSender is intentionally Clone (it travels inside http::Extensions), but accept() consumes the inner oneshot::Sender out of a shared Mutex<Option<_>>; calling accept() on any clone after the first finds None and panics with 'can only accept once'. The documented contract is: across all clones, accept at most once.
Source
Thrown at core/object_store_mem_cache/src/buffer_channel.rs:60
sender: Arc<Mutex<Option<Sender<Slice>>>>,
}
impl BufferSender {
/// Accept that we will have a [`Slice`] available at some point.
///
/// After calling this function, the sender MUST provide a slice at some point. Dropping the returned
/// [handle](BufferSenderAccepted) without doing so will result in an error on the
/// [receiver side](BufferReceiverAccepted).
///
/// # Panic
/// Across all clones, this method must only be called at most once.
pub fn accept(self) -> BufferSenderAccepted {
let Self { accepted, sender } = self;
let maybe_sender = {
let mut guard = sender.lock().unwrap();
guard.take()
};
let sender = maybe_sender.expect("can only accept once");
accepted.store(true, Ordering::SeqCst);
BufferSenderAccepted { sender }
}
}
/// Sender-side in an [accepted](BufferSender::accept) state.
#[derive(Debug)]
pub struct BufferSenderAccepted {
sender: Sender<Slice>,
}
impl BufferSenderAccepted {
/// Send slice.
pub fn send(self, buffer: Slice) {
let Self { sender } = self;
sender.send(buffer).ok();
}
}View on GitHub (pinned to d28e26e048)
Solutions
- Call accept() exactly once per channel and treat the returned BufferSenderAccepted as the single owner
- Do not clone BufferSender casually - clones exist so http::Extensions can carry it, not so several components can accept
- Centralize the accept decision in exactly one middleware layer so retries take the copy path instead
- If two consumers need the data, copy the bytes before hand-off rather than accepting twice
Example fix
// before: two clones both accept let s2 = sender.clone(); let a = sender.accept(); let b = s2.accept(); // panic: can only accept once // after: single owner accepts let a = sender.accept(); // s2 is only carried, never accepted
Defensive patterns
Strategy: validation
Prevention
- Call accept() exactly once per channel and keep BufferSenderAccepted as the single owner
- Remember BufferSender::clone exists for carrying it in http::Extensions, not for multiple accepts
- Make the accept decision in one middleware layer only; retries should take the copy path
- Copy bytes for additional consumers instead of accepting twice
When it happens
Trigger: Calling accept() twice on the same channel (original plus a clone); retry logic that re-accepts after an error; two middleware paths both deciding to accept the buffer from their own clone.
Common situations: New code paths layered onto the cache middleware; tests that run the same request pipeline twice with a reused Extensions set; refactors that moved the accept call into a helper invoked from multiple places.
Related errors
- not poisoned
- never fails
- cannot fit duration into u64
- metric should be in progress
- no metric in progress
AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16).
Data as JSON: /api/errors/180b852e04cf83dc.
Report an issue: GitHub.