tursodatabase/turso · error
MVCC logical pull revision is from the future: client_offset
Error message
MVCC logical pull revision is from the future: client_offset={} server_offset={} What it means
For MVCC logical pulls the server scans its on-disk .db-log and computes end_offset, the end of the last complete transaction. The client_revision (format g1:o<offset>) must parse to an offset <= end_offset; a larger offset means the client has seen a longer logical log than this server holds, so no incremental range can be served and the request fails with HTTP 500.
Source
Thrown at cli/sync_server.rs:699
};
let snapshot = match scan_mvcc_log(&log) {
Ok(snapshot) => snapshot,
Err(err) if is_nonportable_mvcc_log_error(&err) => {
info!(
"logical pull requested but MVCC log is not portable; returning replace-base pages: {err}"
);
return self.handle_logical_fallback(
req,
fallback_revision,
&legacy_current_revision,
db_size,
);
}
Err(err) => return Err(err),
};
let start_offset = parse_mvcc_revision_offset(&req.client_revision, snapshot.end_offset)?;
if start_offset > snapshot.end_offset {
return Err(anyhow!(
"MVCC logical pull revision is from the future: client_offset={} server_offset={}",
start_offset,
snapshot.end_offset
));
}
let start = usize::try_from(start_offset)
.map_err(|_| anyhow!("MVCC logical pull start offset overflows usize"))?;
let end = usize::try_from(snapshot.end_offset)
.map_err(|_| anyhow!("MVCC logical pull end offset overflows usize"))?;
let mut response_body = Vec::new();
let (mvcc_log, body) = if start == end {
(None, Vec::new())
} else {
let crc_seed = if start_offset == 0 {
None
} else {
let seed = snapshot.crc_seed_at(start_offset)?;View on GitHub (pinned to bad083fafb)
Solutions
- Point the client back at the server whose logical log it actually synced from.
- Reset the client database (or clear its persisted revision) so it re-bootstraps from offset 0 against this server.
- If you control the server, answer future revisions with a ReplaceBase page response instead of a 500.
Example fix
// before: revision persisted from another server req.client_revision = "g1:o9500".to_string(); // after: fresh bootstrap; server replies with its current revision req.client_revision = String::new();
Defensive patterns
Strategy: fallback
Try / catch
On 500 containing 'revision is from the future', discard the persisted revision, re-bootstrap the local database, and pull once with an empty client_revision; retrying the same revision will always fail.
Prevention
- Persist and reuse only server-provided revision strings.
- Never point one client at two different sync servers.
- After restoring a server database from backup, reset attached clients' sync state.
When it happens
Trigger: A client synced from a different (or newer production) server whose persisted revision g1:oN has N greater than this server's end_offset; the server's .db/.db-log recreated or restored from an older snapshot while the client kept its revision.
Common situations: Swapping sync endpoints in tests; restoring a server database from backup while reusing client state; running one client against two server instances.
Related errors
- MVCC logical pull offset is not a transaction boundary: {off
- sync_server supports only single-generation MVCC logical pul
- invalid MVCC pull revision generation: {revision}
- invalid MVCC pull revision generation: {revision}: {err}
- invalid MVCC pull revision offset: {revision}: {err}
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/1bc0cd0fe8aecc9e.
Report an issue: GitHub.