{"record":{"id":"3279b68744b83b6d","repo":"neondatabase/neon","slug":"unknown-wal-record-type-for-neon-rmgr","errorCode":null,"errorMessage":"Unknown WAL record type for Neon RMGR: {}","messagePattern":"Unknown WAL record type for Neon RMGR: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/wal_decoder/src/decoder.rs","lineNumber":573,"sourceCode":"                                // the offsets array is omitted if XLOG_HEAP_INIT_PAGE is set\n                                0\n                            } else {\n                                size_of::<u16>() * xlrec.ntuples as usize\n                            };\n                        assert_eq!(offset_array_len, buf.remaining());\n\n                        if (xlrec.flags & pg_constants::XLH_INSERT_ALL_VISIBLE_CLEARED) != 0 {\n                            new_heap_blkno = Some(decoded.blocks[0].blkno);\n                        }\n                    }\n                    pg_constants::XLOG_NEON_HEAP_LOCK => {\n                        let xlrec = v17::rm_neon::XlNeonHeapLock::decode(buf);\n                        if (xlrec.flags & pg_constants::XLH_LOCK_ALL_FROZEN_CLEARED) != 0 {\n                            old_heap_blkno = Some(decoded.blocks[0].blkno);\n                            flags = pg_constants::VISIBILITYMAP_ALL_FROZEN;\n                        }\n                    }\n                    info => anyhow::bail!(\"Unknown WAL record type for Neon RMGR: {}\", info),\n                }\n            }\n            PgMajorVersion::PG15 | PgMajorVersion::PG14 => anyhow::bail!(\n                \"Neon RMGR has no known compatibility with PostgreSQL version {}\",\n                pg_version\n            ),\n        }\n\n        if new_heap_blkno.is_some() || old_heap_blkno.is_some() {\n            let vm_rel = RelTag {\n                forknum: VISIBILITYMAP_FORKNUM,\n                spcnode: decoded.blocks[0].rnode_spcnode,\n                dbnode: decoded.blocks[0].rnode_dbnode,\n                relnode: decoded.blocks[0].rnode_relnode,\n            };\n\n            Ok(Some(MetadataRecord::Neonrmgr(NeonrmgrRecord::ClearVmBits(\n                ClearVmBits {","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/wal_decoder/src/decoder.rs#L555-L591","documentation":"Inside the PG16/PG17 branch of `decode_neonmgr_record`, the record's info byte (masked with XLOG_HEAP_OPMASK) didn't match any known XLOG_NEON_* opcode (INSERT, DELETE, UPDATE/HOT_UPDATE, MULTI_INSERT, LOCK). The wal_decoder in this build simply predates that Neon WAL record type.","triggerScenarios":"Decoding WAL produced by a newer Neon postgres build that emits a new Neon-RMGR record opcode, while the wal_decoder/postgres_ffi in this build doesn't know it yet.","commonSituations":"Pageserver/wal_decoder older than the Neon postgres build writing WAL; ingesting WAL from a canary Neon release; experimental Neon forks adding new Neon record types.","solutions":["Update wal_decoder / postgres_ffi (and the pageserver stack) to a build that supports the new XLOG_NEON_* opcode","Read the numeric info value from the error message to identify which opcode is missing","If the record type never touches VM bits, add an explicit no-op arm for it and upstream the change","Keep WAL producers and decoder versions in lockstep in the pipeline"],"exampleFix":"// before: any unknown Neon opcode aborts decoding\ninfo => anyhow::bail!(\"Unknown WAL record type for Neon RMGR: {}\", info),\n\n// after: explicitly ignore record types that never clear VM bits\ninfo if !affects_visibility_map(info) => { /* no VM bookkeeping needed */ }\ninfo => anyhow::bail!(\"Unknown WAL record type for Neon RMGR: {}\", info),","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match decode_neonmgr_record(buf, decoded, pg_version) {\n    Ok(rec) => { /* ... */ }\n    Err(e) if e.to_string().contains(\"Unknown WAL record type for Neon RMGR\") => {\n        // decoder older than the WAL producer: stop ingesting and upgrade\n        return Err(e.context(\"wal_decoder too old for this WAL stream; upgrade wal_decoder/postgres_ffi\"));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Pin wal_decoder/postgres_ffi versions in lockstep with the Neon postgres build producing WAL","Test decoding against WAL fixtures from every supported Neon build","Treat unknown-record-type errors as a hard stop — never silently skip records"],"tags":["rust","neon","wal-decoding","neon-rmgr","postgres"],"backgroundTag":"unsupported-wal-record-type","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}