neondatabase/neon · critical
Unknown version {}
Error message
Unknown version {} What it means
generate_pg_control dispatches on PgMajorVersion via the dispatch_pgversion! macro, which only compiles bindings for PG14, PG15, PG16, and PG17 (see the pgversions list in postgres_ffi/src/lib.rs). Any other major version falls through to the supplied handler, here anyhow::bail!('Unknown version {}'). The timeline's pg_control data cannot be interpreted by this build.
Source
Thrown at libs/postgres_ffi/src/lib.rs:281
) -> Result<Bytes, SerializeError> {
assert_eq!(segno, lsn.segment_number(WAL_SEGMENT_SIZE));
dispatch_pgversion!(
pg_version,
pgv::xlog_utils::generate_wal_segment(segno, system_id, lsn)
)
}
pub fn generate_pg_control(
pg_control_bytes: &[u8],
checkpoint_bytes: &[u8],
lsn: Lsn,
pg_version: PgMajorVersion,
) -> anyhow::Result<(Bytes, u64, bool)> {
dispatch_pgversion!(
pg_version,
pgv::xlog_utils::generate_pg_control(pg_control_bytes, checkpoint_bytes, lsn),
anyhow::bail!("Unknown version {}", pg_version)
)
}
// PG timeline is always 1, changing it doesn't have any useful meaning in Neon.
//
// NOTE: this is not to be confused with Neon timelines; different concept!
//
// It's a shaky assumption, that it's always 1. We might import a
// PostgreSQL data directory that has gone through timeline bumps,
// for example. FIXME later.
pub const PG_TLI: u32 = 1;
// See TransactionIdIsNormal in transam.h
pub const fn transaction_id_is_normal(id: TransactionId) -> bool {
id > pg_constants::FIRST_NORMAL_TRANSACTION_ID
}
// See TransactionIdPrecedes in transam.cView on GitHub (pinned to 8f60b04da4)
Solutions
- Re-attach/import the timeline on a pageserver build that includes bindings for that major version (update neon)
- If the version looks impossible (e.g. 0 or 65535), suspect a corrupted pg_control or a wrong-offset read -- inspect the raw file
- Recreate the timeline with a supported major version if the data is disposable
- When building from source, confirm the vendored postgres bindings for the needed version are present
Defensive patterns
Strategy: validation
Validate before calling
use pageserver_api::models::Pagebe...; // PgMajorVersion lives in postgres_ffi
fn is_supported_pg_major(v: PgMajorVersion) -> bool {
// must match dispatch_pgversion!'s pgversions list
matches!(
v,
PgMajorVersion::PG14 | PgMajorVersion::PG15 | PgMajorVersion::PG16 | PgMajorVersion::PG17
)
}
// Check before any pg_control interpretation:
anyhow::ensure!(
is_supported_pg_major(timeline_pg_version),
"unsupported PostgreSQL version {timeline_pg_version}; this build supports 14-17"
); Type guard
fn is_supported_pg_major(v: PgMajorVersion) -> bool {
matches!(v, PgMajorVersion::PG14..=PgMajorVersion::PG17)
} Try / catch
match postgres_ffi::generate_pg_control(&control, &checkpoint, lsn, ver) {
Ok(out) => out,
Err(e) if format!("{e:#}").starts_with("Unknown version") => {
// Not retryable: this timeline needs a pageserver built with bindings for that major
tracing::error!(pg_version = %ver, timeline = %timeline_id, "{e:#}");
return Err(e.context(
"timeline uses a PostgreSQL major this build cannot handle; \
reattach on an updated pageserver or recreate the timeline"
));
}
Err(e) => return Err(e),
} Prevention
- Validate pg major versions at tenant attach time and refuse early with an explicit unsupported-version error
- Keep compute images and pageserver builds upgraded together when adopting a new PostgreSQL major
- Expose the supported version range in metrics/logs at startup so mismatches are diagnosable
- Never hand-craft pg_control bytes in tests; reuse fixtures from a supported major
When it happens
Trigger: Attaching or generating pg_control for a timeline whose recorded major version is outside 14-17 (e.g. a PG18 or PG13 data directory imported into this pageserver), or a corrupted/wrong-endianness pg_control whose version field decodes to an unknown value.
Common situations: New PostgreSQL major released before the neon bindings catch up; importing an old tenant archive; downgrading a pageserver binary against newer timelines; tests feeding synthetic pg_control bytes with arbitrary version fields.
Related errors
- Neon RMGR has no known compatibility with PostgreSQL version
- not implemented
- connection to postgres closed
- expected 1 query results, but got {}
- pageserver connection information should be provided
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/442572c920bd201f.
Report an issue: GitHub.