{"record":{"id":"574f4dd818fa2263","repo":"neondatabase/neon","slug":"img-len-size-of-relmapfile-img-len","errorCode":null,"errorMessage":"img.len() != SIZE_OF_RELMAPFILE, img.len()={}","messagePattern":"img\\.len\\(\\) != SIZE_OF_RELMAPFILE, img\\.len\\(\\)=(.+?)","errorType":"exception","errorClass":"BasebackupError","httpStatus":null,"severity":"error","filePath":"pageserver/src/basebackup.rs","lineNumber":648,"sourceCode":"    // Each directory contains a PG_VERSION file, and the default database\n    // directories also contain pg_filenode.map files.\n    //\n    async fn add_dbdir(\n        &mut self,\n        spcnode: u32,\n        dbnode: u32,\n        has_relmap_file: bool,\n    ) -> Result<(), BasebackupError> {\n        let relmap_img = if has_relmap_file {\n            let img = self\n                .timeline\n                .get_relmap_file(spcnode, dbnode, Version::at(self.lsn), self.ctx)\n                .await?;\n\n            if img.len()\n                != dispatch_pgversion!(self.timeline.pg_version, pgv::bindings::SIZEOF_RELMAPFILE)\n            {\n                return Err(BasebackupError::Server(anyhow!(\n                    \"img.len() != SIZE_OF_RELMAPFILE, img.len()={}\",\n                    img.len(),\n                )));\n            }\n\n            Some(img)\n        } else {\n            None\n        };\n\n        if spcnode == GLOBALTABLESPACE_OID {\n            let pg_version_str = self.timeline.pg_version.versionfile_string();\n            let header = new_tar_header(\"PG_VERSION\", pg_version_str.len() as u64)?;\n            self.ar\n                .append(&header, pg_version_str.as_bytes())\n                .await\n                .map_err(|e| BasebackupError::Client(e, \"add_dbdir,PG_VERSION\"))?;\n","sourceCodeStart":630,"sourceCodeEnd":666,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pageserver/src/basebackup.rs#L630-L666","documentation":"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.","triggerScenarios":"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.","commonSituations":"Tenants whose recorded pg_version disagrees with the actual data (manual upgrades, restores); damaged remote-storage objects; relmap key handling bugs.","solutions":["Verify the tenant's recorded pg_version matches the data it actually contains","Retry the basebackup, then inspect the relmap keys in the implicated layers","Validate layer integrity for the tenant","Recover from a healthy copy if corruption is confirmed"],"exampleFix":"// before: length checked only at backup time\n// after: check immediately after retrieval with pg-version context\nlet expected = dispatch_pgversion!(timeline.pg_version, pgv::bindings::SIZEOF_RELMAPFILE);\nif img.len() != expected {\n    tracing::error!(len = img.len(), expected, \"relmap size mismatch\");\n    anyhow::bail!(\"relmap image for db {dbnode} has wrong size\");\n}","handlingStrategy":"validation","validationCode":"let expected = dispatch_pgversion!(timeline.pg_version, pgv::bindings::SIZEOF_RELMAPFILE);\nanyhow::ensure!(\n    img.len() == expected,\n    \"relmap len {} != expected {expected} for pg {}\",\n    img.len(),\n    timeline.pg_version\n);","typeGuard":"fn is_valid_relmap(img: &bytes::Bytes, pg_version: u32) -> bool {\n    img.len() == dispatch_pgversion!(pg_version, pgv::bindings::SIZEOF_RELMAPFILE)\n}","tryCatchPattern":"Err(e) if e.to_string().contains(\"SIZE_OF_RELMAPFILE\") => {\n    // suspect pg_version mismatch or layer corruption; validate tenant metadata\n}","preventionTips":["Keep tenant pg_version metadata authoritative and validated after upgrades","Include relmap size checks in periodic tenant scrubbing","Treat any relmap abort as potential corruption, not a transient error"],"tags":["pageserver","basebackup","relmap","pg-version","data-integrity"],"backgroundTag":"corrupt-page-image","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}