databendlabs/databend · critical

current_page has taken

Error message

current_page has taken

What it means

In `sized_spsc` (the sized single-producer/single-consumer block channel used by HTTP query result streaming), `try_add_block` assumes `self.current_page` is always `Some` and unwraps it with `.expect("current_page has taken")`. The page is only moved out (taken) when the producer transitions a full page to the consumer; if the producer then appends another block, the invariant is broken. This signals a producer-side state machine bug or race where blocks are added after the page hand-off without installing a new page.

Solutions

  1. Reproduce with the query whose result stream panics and check whether it stops sending after the consumer finished; ensure the producer checks send/recv stop flags before each append.
  2. Fix `try_add_block` to re-create or return `SendFail::Closed` when `current_page` is `None` instead of unwrapping.
  3. Audit the page hand-off code path (`is_pages_full` → page swap) for a missing `current_page = Some(new_page)` assignment.
  4. Upgrade Databend if this arises during normal HTTP query streaming, as it indicates a fixed channel regression.

Example fix

// before
let page_builder = self.current_page.as_mut().expect("current_page has taken");

// after
let page_builder = match self.current_page.as_mut() {
    Some(p) => p,
    None => return Err(SendFail::Closed), // page handed off; no new page installed
};
Defensive patterns

Strategy: try-catch

Type guard

fn has_current_page(q: &SizedSpsc) -> bool { q.current_page.is_some() }

Try / catch

// Producer-side: treat missing page as closed channel instead of panicking
let page_builder = match self.current_page.as_mut() {
    Some(p) => p,
    None => return Err(SendFail::Closed),
};

Prevention

When it happens

Trigger: Calling `try_add_block` after `current_page` was taken (page handed to the receiver) and before/without a replacement page being created — e.g., blocks pushed after `is_pages_full` handling raced ahead, or after the receiver set stop flags in a window the guard didn't cover.

Common situations: HTTP query clients reading results while the producer keeps streaming; extremely fast consumers causing frequent page hand-offs that expose the race; dev/regression builds where page-swap logic in the send path was refactored.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/e958bdd84e45f98b. Report an issue: GitHub.

Appendix: source

Thrown at src/query/service/src/servers/http/v1/query/sized_spsc.rs:111

            })
            .sum()
    }

    fn has_page_ready(&self) -> bool {
        !self.pages.is_empty()
    }

    fn is_pages_full(&self, reserve: usize) -> bool {
        self.pages_rows() + reserve > self.max_rows
    }

    fn try_add_block(&mut self, mut block: DataBlock) -> result::Result<(), SendFail> {
        if self.is_recv_stopped || self.is_send_stopped {
            return Err(SendFail::Closed);
        }

        loop {
            let page_builder = self.current_page.as_mut().expect("current_page has taken");

            let remain = page_builder.try_append_block(block);
            if !page_builder.has_capacity() {
                let rows = page_builder.num_rows();
                if self.is_pages_full(rows) {
                    return Err(SendFail::Full {
                        page: self
                            .current_page
                            .take()
                            .expect("current_page has taken")
                            .into_page(),
                        remain,
                    });
                }
                let page = self
                    .current_page
                    .replace(PageBuilder::new(self.page_rows))
                    .expect("current_page has taken")

View on GitHub (pinned to 288d84d76e)