neondatabase/neon · error · BasebackupError

img.len() != SIZE_OF_RELMAPFILE, img.len()={}

Error message

img.len() != SIZE_OF_RELMAPFILE, img.len()={}

What it means

When a basebackup includes a database's relmapper file (pg_filenode.map), the image fetched via get_relmap_file must equal SIZEOF_RELMAPFILE for the timeline's pg version, selected through dispatch_pgversion!. A different length aborts the basebackup, because shipping a wrong-sized relmap would corrupt the compute's catalog mapping.

Source

Thrown at pageserver/src/basebackup.rs:648

    // Each directory contains a PG_VERSION file, and the default database
    // directories also contain pg_filenode.map files.
    //
    async fn add_dbdir(
        &mut self,
        spcnode: u32,
        dbnode: u32,
        has_relmap_file: bool,
    ) -> Result<(), BasebackupError> {
        let relmap_img = if has_relmap_file {
            let img = self
                .timeline
                .get_relmap_file(spcnode, dbnode, Version::at(self.lsn), self.ctx)
                .await?;

            if img.len()
                != dispatch_pgversion!(self.timeline.pg_version, pgv::bindings::SIZEOF_RELMAPFILE)
            {
                return Err(BasebackupError::Server(anyhow!(
                    "img.len() != SIZE_OF_RELMAPFILE, img.len()={}",
                    img.len(),
                )));
            }

            Some(img)
        } else {
            None
        };

        if spcnode == GLOBALTABLESPACE_OID {
            let pg_version_str = self.timeline.pg_version.versionfile_string();
            let header = new_tar_header("PG_VERSION", pg_version_str.len() as u64)?;
            self.ar
                .append(&header, pg_version_str.as_bytes())
                .await
                .map_err(|e| BasebackupError::Client(e, "add_dbdir,PG_VERSION"))?;

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Verify the tenant's recorded pg_version matches the data it actually contains
  2. Retry the basebackup, then inspect the relmap keys in the implicated layers
  3. Validate layer integrity for the tenant
  4. Recover from a healthy copy if corruption is confirmed

Example fix

// before: length checked only at backup time
// after: check immediately after retrieval with pg-version context
let expected = dispatch_pgversion!(timeline.pg_version, pgv::bindings::SIZEOF_RELMAPFILE);
if img.len() != expected {
    tracing::error!(len = img.len(), expected, "relmap size mismatch");
    anyhow::bail!("relmap image for db {dbnode} has wrong size");
}
Defensive patterns

Strategy: validation

Validate before calling

let expected = dispatch_pgversion!(timeline.pg_version, pgv::bindings::SIZEOF_RELMAPFILE);
anyhow::ensure!(
    img.len() == expected,
    "relmap len {} != expected {expected} for pg {}",
    img.len(),
    timeline.pg_version
);

Type guard

fn is_valid_relmap(img: &bytes::Bytes, pg_version: u32) -> bool {
    img.len() == dispatch_pgversion!(pg_version, pgv::bindings::SIZEOF_RELMAPFILE)
}

Try / catch

Err(e) if e.to_string().contains("SIZE_OF_RELMAPFILE") => {
    // suspect pg_version mismatch or layer corruption; validate tenant metadata
}

Prevention

When it happens

Trigger: The stored relmap image has an unexpected size: truncated or corrupt layer data, or timeline.pg_version recorded with the wrong major so the dispatch picks the wrong SIZEOF_RELMAPFILE constant.

Common situations: Tenants whose recorded pg_version disagrees with the actual data (manual upgrades, restores); damaged remote-storage objects; relmap key handling bugs.

Related errors


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