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

  1. Call accept() exactly once per channel and treat the returned BufferSenderAccepted as the single owner
  2. Do not clone BufferSender casually - clones exist so http::Extensions can carry it, not so several components can accept
  3. Centralize the accept decision in exactly one middleware layer so retries take the copy path instead
  4. 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

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


AI-assisted analysis of influxdata/influxdb@d28e26e048 (2026-08-16). Data as JSON: /api/errors/180b852e04cf83dc. Report an issue: GitHub.