neondatabase/neon · error
Unknown RMGR {} for Heap decoding
Error message
Unknown RMGR {} for Heap decoding What it means
`decode_heapam_record` in wal_decoder tracks visibility-map bit updates for heap records, per PG major version. Within the PG14 branch it only accepts `RM_HEAP_ID` and `RM_HEAP2_ID`; this bail fires when `decoded.xl_rmid` is anything else. Since callers are expected to route only Heap/Heap2 records here, hitting it means a misrouted record or a pg_version that doesn't match the WAL being decoded.
Source
Thrown at libs/wal_decoder/src/decoder.rs:272
// the offsets array is omitted if XLOG_HEAP_INIT_PAGE is set
0
} else {
size_of::<u16>() * xlrec.ntuples as usize
};
assert_eq!(offset_array_len, buf.remaining());
if (xlrec.flags & pg_constants::XLH_INSERT_ALL_VISIBLE_CLEARED) != 0 {
new_heap_blkno = Some(decoded.blocks[0].blkno);
}
} else if info == pg_constants::XLOG_HEAP2_LOCK_UPDATED {
let xlrec = v14::XlHeapLockUpdated::decode(buf);
if (xlrec.flags & pg_constants::XLH_LOCK_ALL_FROZEN_CLEARED) != 0 {
old_heap_blkno = Some(decoded.blocks[0].blkno);
flags = pg_constants::VISIBILITYMAP_ALL_FROZEN;
}
}
} else {
anyhow::bail!("Unknown RMGR {} for Heap decoding", decoded.xl_rmid);
}
}
PgMajorVersion::PG15 => {
if decoded.xl_rmid == pg_constants::RM_HEAP_ID {
let info = decoded.xl_info & pg_constants::XLOG_HEAP_OPMASK;
if info == pg_constants::XLOG_HEAP_INSERT {
let xlrec = v15::XlHeapInsert::decode(buf);
assert_eq!(0, buf.remaining());
if (xlrec.flags & pg_constants::XLH_INSERT_ALL_VISIBLE_CLEARED) != 0 {
new_heap_blkno = Some(decoded.blocks[0].blkno);
}
} else if info == pg_constants::XLOG_HEAP_DELETE {
let xlrec = v15::XlHeapDelete::decode(buf);
if (xlrec.flags & pg_constants::XLH_DELETE_ALL_VISIBLE_CLEARED) != 0 {
new_heap_blkno = Some(decoded.blocks[0].blkno);
}
} else if info == pg_constants::XLOG_HEAP_UPDATEView on GitHub (pinned to 8f60b04da4)
Solutions
- Fix the dispatch site: only records with xl_rmid RM_HEAP_ID or RM_HEAP2_ID should reach heap decoding
- Verify the pg_version passed to the decoder matches the cluster that produced the WAL
- If adding a new RMGR, add its own handler branch instead of routing through decode_heapam_record
- Log decoded.xl_rmid and xl_info upstream to identify the offending record
Example fix
// before: every record is sent to heap decoding
decode_heapam_record(buf, decoded, pg_version)?;
// after: only Heap/Heap2 records
if matches!(decoded.xl_rmid, pg_constants::RM_HEAP_ID | pg_constants::RM_HEAP2_ID) {
decode_heapam_record(buf, decoded, pg_version)?;
} Defensive patterns
Strategy: validation
Validate before calling
if !matches!(decoded.xl_rmid, pg_constants::RM_HEAP_ID | pg_constants::RM_HEAP2_ID) {
// not a heap record: route to its own handler or skip
return Ok(None);
} Type guard
fn is_heap_record(rmid: u8) -> bool {
matches!(rmid, pg_constants::RM_HEAP_ID | pg_constants::RM_HEAP2_ID)
} Prevention
- Keep the pg_version used for decoding tied to the WAL source, not a free parameter
- When adding RMGRs, extend the dispatcher match instead of the heap branch
- Fuzz the decoder with arbitrary rmids to assert correct routing
When it happens
Trigger: Decoding a PG14 WAL stream where a record with `xl_rmid` other than RM_HEAP_ID/RM_HEAP2_ID is fed into the heap decoder — new dispatch code forwarding all record types, or `pg_version` mismatched with the actual WAL.
Common situations: Extending wal_decoder to new record types and misrouting; an off-by-one version mapping after a PG release; fuzz tests feeding arbitrary rmids into the heap path.
Related errors
- Unknown WAL record type for Neon RMGR: {}
- Neon RMGR has no known compatibility with PostgreSQL version
- pg_ctl failed, exit code: {}, stdout: {}, stderr: {}
- file cache size query returned no rows
- max file cache size query returned no rows
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/842f0b720e154e53.
Report an issue: GitHub.