neondatabase/neon · error · BasebackupError

invalid {:?} record: block.len()={}

Error message

invalid {:?} record: block.len()={}

What it means

The same SLRU segment builder validates pg_multixact_offsets and pg_multixact_members page images: they must be exactly BLCKSZ. Any other length means the multixact SLRU page reconstructed from layers is malformed, and the basebackup aborts with the SLRU kind and offending length in the message.

Source

Thrown at pageserver/src/basebackup.rs:268

            total_blocks: 0,
        }
    }

    async fn add_block(&mut self, key: &Key, block: Bytes) -> Result<(), BasebackupError> {
        let (kind, segno, _) = key.to_slru_block()?;

        match kind {
            SlruKind::Clog => {
                if !(block.len() == BLCKSZ as usize || block.len() == BLCKSZ as usize + 8) {
                    return Err(BasebackupError::Server(anyhow!(
                        "invalid SlruKind::Clog record: block.len()={}",
                        block.len()
                    )));
                }
            }
            SlruKind::MultiXactMembers | SlruKind::MultiXactOffsets => {
                if block.len() != BLCKSZ as usize {
                    return Err(BasebackupError::Server(anyhow!(
                        "invalid {:?} record: block.len()={}",
                        kind,
                        block.len()
                    )));
                }
            }
        }

        let segment = (kind, segno);
        match self.current_segment {
            None => {
                self.current_segment = Some(segment);
                self.buf
                    .extend_from_slice(block.slice(..BLCKSZ as usize).as_ref());
            }
            Some(current_seg) if current_seg == segment => {
                self.buf
                    .extend_from_slice(block.slice(..BLCKSZ as usize).as_ref());

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Check pageserver logs for the failing key and layer
  2. Retry the basebackup at a different LSN to see whether the damage is LSN-bounded
  3. Validate or re-download the implicated layers from remote storage
  4. If corruption is confirmed, recover from a healthy branch or backup

Example fix

// before: size checked only inside the builder during basebackup
// after: validate on read so callers learn the key, not just the length
let (kind, segno, blk) = key.to_slru_block()?;
if kind.is_multixact() && img.len() != 8192 {
    anyhow::bail!("corrupt {kind:?} page seg {segno} blk {blk}: len {}", img.len());
}
Defensive patterns

Strategy: validation

Validate before calling

// scrub multixact SLRU blocks ahead of a backup window
for kind in [SlruKind::MultiXactOffsets, SlruKind::MultiXactMembers] {
    for key in scan_slru_keys(kind).await? {
        let img = timeline.get(key, lsn, ctx).await?;
        if img.len() != 8192 {
            alert_corruption(ttid, key, img.len());
        }
    }
}

Type guard

fn is_valid_slru_page(kind: SlruKind, img: &bytes::Bytes) -> bool {
    match kind {
        SlruKind::Clog => matches!(img.len(), 8192 | 8200),
        _ => img.len() == 8192,
    }
}

Try / catch

Err(e) if e.to_string().contains("record: block.len()") => {
    // parse the kind and length from the message; open a corruption investigation
}

Prevention

When it happens

Trigger: A MultiXactMembers or MultiXactOffsets block image with size != 8192 served during basebackup: layer corruption, a reconstruction bug for multixact keys, or page-layout skew from version changes.

Common situations: Workloads with heavy shared-row locking (multixacts) hitting a damaged layer; branches spanning neon versions with multixact format changes.

Related errors


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