neondatabase/neon · error

unexpected be message kind in response to getpage request: {

Error message

unexpected be message kind in response to getpage request: {}

What it means

`PagestreamReceiver::getpage_recv` matches on the next backend message and expects `GetPage`; receiving Exists, Nblocks, DbSize, GetSlruSegment (or the testing-only Test variant) means the response kind doesn't pair with the getpage request that was sent. The pagestream protocol is strictly request/response, so this indicates the recv method was mismatched to the request or responses were consumed out of order.

Source

Thrown at pageserver/client/src/page_service.rs:241

impl PagestreamReceiver {
    // TODO: maybe make this impl Stream instead for better composability?
    pub async fn recv(&mut self) -> anyhow::Result<PagestreamBeMessage> {
        let next: Option<Result<bytes::Bytes, _>> = self.stream.next().await;
        let next: bytes::Bytes = next.unwrap()?;
        PagestreamBeMessage::deserialize(next)
    }

    pub async fn getpage_recv(&mut self) -> anyhow::Result<PagestreamGetPageResponse> {
        let next: PagestreamBeMessage = self.recv().await?;
        match next {
            PagestreamBeMessage::GetPage(p) => Ok(p),
            PagestreamBeMessage::Error(e) => anyhow::bail!("Error: {:?}", e),
            PagestreamBeMessage::Exists(_)
            | PagestreamBeMessage::Nblocks(_)
            | PagestreamBeMessage::DbSize(_)
            | PagestreamBeMessage::GetSlruSegment(_) => {
                anyhow::bail!(
                    "unexpected be message kind in response to getpage request: {}",
                    next.kind()
                )
            }
            #[cfg(feature = "testing")]
            PagestreamBeMessage::Test(_) => {
                anyhow::bail!(
                    "unexpected be message kind in response to getpage request: {}",
                    next.kind()
                )
            }
        }
    }
}

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Pair each request with its matching recv function (e.g. exists request → exists-style recv, nblocks request → nblocks recv)
  2. Serialize requests on a single pagestream connection: send, recv, then send the next
  3. Use the message's `kind()` in the error output to see which response actually arrived and infer which request it belongs to
  4. Add a debug assertion logging request/response kinds during development

Example fix

// before: mismatched request/response pairing
pagestream.exists_send(&req).await?;
let page = pagestream.getpage_recv().await?; // unexpected be message kind

// after: match recv to the request sent
pagestream.exists_send(&req).await?;
let exists = pagestream.exists_recv().await?;
Defensive patterns

Strategy: validation

Validate before calling

// ensure the response kind matches the request before consuming it
let msg = pagestream.recv().await?;
if msg.kind() != BeaconKind::GetPage {
    anyhow::bail!("expected GetPage response, got {} — check send/recv pairing", msg.kind());
}

Type guard

fn is_getpage_response(msg: &PagestreamBeMessage) -> bool {
    matches!(msg, PagestreamBeMessage::GetPage(_))
}

Prevention

When it happens

Trigger: Sending a different request kind (exists/nblocks/dbsize/getslrusegment) but calling `getpage_recv` for the reply; or interleaving multiple requests on one receiver so a later response is read as the getpage answer.

Common situations: Copy-pasted client code pairing the wrong send/recv helpers; refactors that changed request order; concurrent access to a single PagestreamReceiver without serialization.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/481c091677d0ae74. Report an issue: GitHub.