neondatabase/neon · error
Unknown WAL record type for Neon RMGR: {}
Error message
Unknown WAL record type for Neon RMGR: {} What it means
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.
Source
Thrown at libs/wal_decoder/src/decoder.rs:573
// 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);
}
}
pg_constants::XLOG_NEON_HEAP_LOCK => {
let xlrec = v17::rm_neon::XlNeonHeapLock::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;
}
}
info => anyhow::bail!("Unknown WAL record type for Neon RMGR: {}", info),
}
}
PgMajorVersion::PG15 | PgMajorVersion::PG14 => anyhow::bail!(
"Neon RMGR has no known compatibility with PostgreSQL version {}",
pg_version
),
}
if new_heap_blkno.is_some() || old_heap_blkno.is_some() {
let vm_rel = RelTag {
forknum: VISIBILITYMAP_FORKNUM,
spcnode: decoded.blocks[0].rnode_spcnode,
dbnode: decoded.blocks[0].rnode_dbnode,
relnode: decoded.blocks[0].rnode_relnode,
};
Ok(Some(MetadataRecord::Neonrmgr(NeonrmgrRecord::ClearVmBits(
ClearVmBits {View on GitHub (pinned to 8f60b04da4)
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
Example fix
// before: any unknown Neon opcode aborts decoding
info => anyhow::bail!("Unknown WAL record type for Neon RMGR: {}", info),
// after: explicitly ignore record types that never clear VM bits
info if !affects_visibility_map(info) => { /* no VM bookkeeping needed */ }
info => anyhow::bail!("Unknown WAL record type for Neon RMGR: {}", info), Defensive patterns
Strategy: try-catch
Try / catch
match decode_neonmgr_record(buf, decoded, pg_version) {
Ok(rec) => { /* ... */ }
Err(e) if e.to_string().contains("Unknown WAL record type for Neon RMGR") => {
// decoder older than the WAL producer: stop ingesting and upgrade
return Err(e.context("wal_decoder too old for this WAL stream; upgrade wal_decoder/postgres_ffi"));
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unknown RMGR {} for Heap decoding
- 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/3279b68744b83b6d.
Report an issue: GitHub.