tursodatabase/turso · error
invalid MVCC pull revision generation: {revision}
Error message
invalid MVCC pull revision generation: {revision} What it means
client_revision strings containing ":o" are parsed as g<generation>:o<offset>, and the generation segment must begin with a literal 'g'. Any other prefix — "2:o5", "gen1:o5", "rev:o0" — is rejected as an invalid generation with HTTP 500 before any log data is served.
Source
Thrown at cli/sync_server.rs:925
"MVCC logical pull is not supported for in-memory sync server databases"
));
}
let path = if let Some(rest) = db_path.strip_prefix("file:") {
rest.split_once('?').map_or(rest, |(path, _)| path)
} else {
db_path
};
Ok(PathBuf::from(path))
}
fn parse_mvcc_revision_offset(revision: &str, legacy_default: u64) -> Result<u64> {
if revision.is_empty() {
return Ok(0);
}
if let Some((generation, offset)) = revision.split_once(":o") {
let generation = generation
.strip_prefix('g')
.ok_or_else(|| anyhow!("invalid MVCC pull revision generation: {revision}"))?
.parse::<u64>()
.map_err(|err| anyhow!("invalid MVCC pull revision generation: {revision}: {err}"))?;
if generation != 1 {
return Err(anyhow!(
"sync_server supports only single-generation MVCC logical pulls: {revision}"
));
}
return offset
.parse::<u64>()
.map_err(|err| anyhow!("invalid MVCC pull revision offset: {revision}: {err}"));
}
// Older page bootstrap responses from this test server used WAL frame
// numbers. Treat them as "the page snapshot already includes the current
// logical log" so the required follow-up logical pull becomes a no-op.
Ok(legacy_default)
}
fn scan_mvcc_log(log: &[u8]) -> Result<MvccLogSnapshot> {View on GitHub (pinned to bad083fafb)
Solutions
- Only send revisions the server previously returned (the server_revision field of the last response).
- Send an empty client_revision to pull from offset 0.
- For page-bootstrapped clients, plain numeric WAL-frame revisions (no ':o') are accepted as the legacy default — use that form.
Example fix
// before req.client_revision = "1:o128".to_string(); // after req.client_revision = "g1:o128".to_string();
Defensive patterns
Strategy: validation
Validate before calling
fn is_server_revision(rev: &str) -> bool {
if rev.is_empty() || rev.chars().all(|c| c.is_ascii_digit()) {
return true; // empty, or legacy WAL-frame revision
}
matches!(
rev.strip_prefix('g').and_then(|r| r.split_once(":o")),
Some((g, o)) if g.parse::<u64>().is_ok() && o.parse::<u64>().is_ok()
)
} Try / catch
On 500 'invalid MVCC pull revision generation', do not retry the same string: re-read the last server_revision from the previous response, or send an empty client_revision to restart from offset 0.
Prevention
- Only send revisions the server itself returned.
- Validate the g<digits>:o<digits> grammar before sending custom values.
- Keep legacy numeric revisions only for page-bootstrapped clients.
When it happens
Trigger: Hand-built or legacy revision strings sent to /pull-updates with stream_kind=MvccLogicalLog; clients composing the revision grammar themselves instead of reusing server-provided strings.
Common situations: Custom sync clients, ported scripts, and tests that fabricate revision strings; protocol drift where another component emits a different revision format.
Related errors
- invalid MVCC pull revision generation: {revision}: {err}
- invalid MVCC pull revision offset: {revision}: {err}
- MVCC logical pull revision is from the future: client_offset
- MVCC logical pull offset is not a transaction boundary: {off
- sync_server supports only single-generation MVCC logical pul
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/0650f5996071afdc.
Report an issue: GitHub.