neondatabase/neon · error · BasebackupError

backup_prev {backup_prev} != provided_prev_lsn {provided_pre

Error message

backup_prev {backup_prev} != provided_prev_lsn {provided_prev_lsn}

What it means

While serving a basebackup, the pageserver derives prev_lsn from the timeline state: Lsn(0) when the backup targets a client-specified LSN, or the last record's prev for an end-of-timeline backup. If the request also carries prev_lsn, it must equal the derived non-zero value; otherwise the request is rejected because client and server disagree about the WAL position the backup is anchored to.

Source

Thrown at pageserver/src/basebackup.rs:151

        // provide prev_lsn. (get_last_record_rlsn() might return it as
        // zero, though, if no WAL has been generated on this timeline
        // yet.)
        let end_of_timeline = timeline.get_last_record_rlsn();
        if req_lsn == end_of_timeline.last {
            (end_of_timeline.prev, req_lsn)
        } else {
            (Lsn(0), req_lsn)
        }
    } else {
        // Backup was requested at end of the timeline.
        let end_of_timeline = timeline.get_last_record_rlsn();
        (end_of_timeline.prev, end_of_timeline.last)
    };

    // Consolidate the derived and the provided prev_lsn values
    let prev_record_lsn = if let Some(provided_prev_lsn) = prev_lsn {
        if backup_prev != Lsn(0) && backup_prev != provided_prev_lsn {
            return Err(BasebackupError::Server(anyhow!(
                "backup_prev {backup_prev} != provided_prev_lsn {provided_prev_lsn}"
            )));
        }
        provided_prev_lsn
    } else {
        backup_prev
    };

    info!(
        "taking basebackup lsn={lsn}, prev_lsn={prev_record_lsn} \
        (full_backup={full_backup}, replica={replica}, gzip={gzip_level:?})",
    );
    let span = info_span!("send_tarball", backup_lsn=%lsn);

    let io_concurrency = IoConcurrency::spawn_from_conf(
        timeline.conf.get_vectored_concurrent_io,
        timeline
            .gate

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Retry the basebackup so the client resynchronizes with current timeline state, or omit prev_lsn so the server-derived value is used
  2. Verify compute and pageserver run matching neon versions
  3. Confirm exactly one pageserver is attached and active for the timeline
  4. If it persists, capture both LSN values from the message and report it; it usually indicates an LSN bookkeeping bug

Example fix

// before: client always echoes its cached prev_lsn
let req = BasebackupRequest { lsn, prev_lsn: Some(cached_prev), .. };
// after: on 'backup_prev != provided_prev_lsn', retry letting the server derive it
match send_basebackup(lsn, Some(cached_prev)).await {
    Err(e) if e.to_string().contains("backup_prev") => {
        send_basebackup(lsn, None).await?
    }
    r => r?,
}
Defensive patterns

Strategy: retry

Validate before calling

// before requesting, reconcile the client's last-record view with the server
let server_info = timeline_info_from_mgmt_api().await?;
if client_prev != server_info.last_record_lsn.prev {
    // refresh client state instead of sending a stale prev_lsn
}

Try / catch

match pageserver_basebackup(req).await {
    Ok(stream) => stream,
    Err(e) if e.to_string().contains("backup_prev") => {
        // prev_lsn disagreement: resync and retry without prev_lsn
        pageserver_basebackup(req.with_prev_lsn(None)).await?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: End-of-timeline basebackups where the compute's idea of the last record differs from the pageserver's: after failover or re-attach, timeline migration to another pageserver, LSN regression after restoration, or WAL ingestion racing the request; also version skew in how prev_lsn is computed.

Common situations: A compute reconnecting to a recovered pageserver; a timeline attached to two pageservers (split brain); old compute images paired with new pageservers.

Related errors


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