tursodatabase/turso · error

invalid MVCC pull revision generation: {revision}: {err}

Error message

invalid MVCC pull revision generation: {revision}: {err}

What it means

Same revision grammar as the sibling error: the generation segment starts with 'g' but the remaining digits do not parse as u64 — e.g. "g:o9", "g1x:o5", "g-1:o3". The underlying ParseInt error text is appended to the message so the malformed segment is identifiable.

Source

Thrown at cli/sync_server.rs:927

    }
    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> {
    if log.is_empty() {
        return Ok(MvccLogSnapshot {

View on GitHub (pinned to bad083fafb)

Solutions

  1. Validate the revision against the g<digits>:o<digits> grammar before sending.
  2. Echo back server-provided revision strings verbatim instead of reconstructing them.
  3. Send an empty client_revision when in doubt; the server then defines the starting offset.

Example fix

// before
req.client_revision = "gx1: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;
    }
    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' with a parse error, replace the malformed revision with the last server-provided one (or empty string) and retry once.

Prevention

When it happens

Trigger: Hand-edited revision strings where the generation contains non-numeric characters, a sign, or is empty after the 'g'.

Common situations: Manual state surgery on persisted sync state; tests and scripts that template the revision string from unvalidated input.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/b281a56db827ced2. Report an issue: GitHub.