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.c

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Re-attach/import the timeline on a pageserver build that includes bindings for that major version (update neon)
  2. If the version looks impossible (e.g. 0 or 65535), suspect a corrupted pg_control or a wrong-offset read -- inspect the raw file
  3. Recreate the timeline with a supported major version if the data is disposable
  4. 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

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


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/442572c920bd201f. Report an issue: GitHub.