neondatabase/neon · error · BasebackupError
invalid SlruKind::Clog record: block.len()={}
Error message
invalid SlruKind::Clog record: block.len()={} What it means
During basebackup SLRU assembly, every clog (pg_xact) page image must be exactly BLCKSZ (8192) or BLCKSZ+8 (8200, the long-page-header form). Any other length means the image reconstructed from timeline layers is malformed, so the pageserver aborts the basebackup rather than shipping corrupt data.
Source
Thrown at pageserver/src/basebackup.rs:260
where
W: AsyncWrite + Send + Sync + Unpin,
{
fn new(ar: &'a mut Builder<&'b mut W>) -> Self {
Self {
ar,
buf: Vec::new(),
current_segment: None,
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 {View on GitHub (pinned to 8f60b04da4)
Solutions
- Check pageserver logs for layer read or reconstruction errors near the failure
- Retry the basebackup at a different LSN or after a restart to rule out transient reads
- Validate the tenant's layers and their remote copies
- If corruption is confirmed, rebuild the tenant from a healthy branch or backup
Example fix
// before: trust any returned image
let img = timeline.get_rel_page(key, Version::at(lsn), ctx).await?;
// after: validate size at read time to catch corruption early
if !matches!(img.len(), 8192 | 8200) {
tracing::error!(?key, len = img.len(), "unexpected clog page size");
anyhow::bail!("corrupt clog image for {key}");
} Defensive patterns
Strategy: validation
Validate before calling
// periodic scrub: verify every clog block image size without taking a basebackup
for key in scan_slru_keys(SlruKind::Clog).await? {
let img = timeline.get(key, lsn, ctx).await?;
if !matches!(img.len(), 8192 | 8200) {
alert_corruption(ttid, key, img.len());
}
} Type guard
fn is_valid_clog_page(img: &bytes::Bytes) -> bool {
matches!(img.len(), 8192 | 8200)
} Try / catch
match serve_basebackup(request).await {
Ok(b) => b,
Err(e) if e.to_string().contains("invalid SlruKind::Clog record") => {
// schedule layer validation; surface a corruption alert, do not retry
corruption_alert(ttid, e).await;
return Err(e);
}
Err(e) => return Err(e),
} Prevention
- Run periodic tenant and layer validation jobs
- Monitor basebackup failures per tenant for corruption signatures
- Verify remote-storage object checksums after large uploads
- Keep neon versions consistent when writing long-lived branches
When it happens
Trigger: A clog block image of unexpected size returned for a key decoded by to_slru_block(): corrupted layer files (local or remote), a page-image reconstruction bug, or pg-version dispatch producing the wrong page layout.
Common situations: Bit rot or partial writes in layer storage; corrupted remote-storage objects after transfer failures; old branches written by different neon versions.
Related errors
- invalid {:?} record: block.len()={}
- img.len() != SIZE_OF_RELMAPFILE, img.len()={}
- backup_prev {backup_prev} != provided_prev_lsn {provided_pre
- pageserver connection information should be provided
- shard {shard_index} missing from pageserver_connection_info
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/a6ce7468036928ec.
Report an issue: GitHub.