{"record":{"id":"af32677d29180367","repo":"tursodatabase/turso","slug":"mvcc-logical-pull-offset-is-not-a-transaction-boun","errorCode":null,"errorMessage":"MVCC logical pull offset is not a transaction boundary: {offset}","messagePattern":"MVCC logical pull offset is not a transaction boundary: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"cli/sync_server.rs","lineNumber":891,"sourceCode":"\nstruct HttpResponse {\n    status: u16,\n    content_type: String,\n    body: Vec<u8>,\n}\n\nstruct MvccLogSnapshot {\n    end_offset: u64,\n    crc_by_offset: Vec<(u64, u32)>,\n}\n\nimpl MvccLogSnapshot {\n    fn crc_seed_at(&self, offset: u64) -> Result<u32> {\n        self.crc_by_offset\n            .iter()\n            .find_map(|(boundary, crc)| (*boundary == offset).then_some(*crc))\n            .ok_or_else(|| {\n                anyhow!(\"MVCC logical pull offset is not a transaction boundary: {offset}\")\n            })\n    }\n}\n\nfn logical_log_path(db_path: &str) -> Result<PathBuf> {\n    Ok(db_file_path(db_path)?.with_extension(\"db-log\"))\n}\n\nfn is_in_memory_db_path(db_path: &str) -> bool {\n    db_path == \":memory:\"\n}\n\nfn db_file_path(db_path: &str) -> Result<PathBuf> {\n    if is_in_memory_db_path(db_path) {\n        return Err(anyhow!(\n            \"MVCC logical pull is not supported for in-memory sync server databases\"\n        ));\n    }","sourceCodeStart":873,"sourceCodeEnd":909,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/cli/sync_server.rs#L873-L909","documentation":"Incremental logical pulls from a non-zero offset need the crc32c seed recorded at that exact transaction boundary; MvccLogSnapshot.crc_by_offset only contains offsets where a complete transaction ends (the header offset plus each frame end). A client revision pointing into the middle of a transaction frame cannot be resumed — the receiver's CRC chain would not match — so crc_seed_at fails and the pull returns HTTP 500.","triggerScenarios":"client_revision g1:oN where N is not a frame end recorded by scan_mvcc_log: hand-crafted revisions, offsets computed by a different tool, or a .db-log that was rewritten or compacted so a previously valid boundary no longer is.","commonSituations":"Unit tests using arbitrary byte offsets; persisting a hand-computed offset instead of the server's revision string; swapping .db-log files between runs.","solutions":["Always echo back the exact server_revision string from the previous /pull-updates response; it is always a transaction boundary.","Send an empty client_revision to restart from offset 0 and receive the full logical range.","If client or server state is stale, remove the server's .db-log: the server then falls back to replace-base pages and clients re-bootstrap."],"exampleFix":"// before: guessed offset that may sit inside a transaction\nreq.client_revision = format!(\"g1:o{}\", arbitrary_byte_offset);\n\n// after: use the server-provided revision verbatim\nreq.client_revision = last_response.server_revision.clone(); // e.g. \"g1:o1048576\"","handlingStrategy":"fallback","validationCode":"fn is_known_boundary(rev: &str, boundaries: &[u64]) -> bool {\n    match rev.strip_prefix(\"g1:o\").and_then(|o| o.parse::<u64>().ok()) {\n        Some(off) => off == 0 || boundaries.contains(&off),\n        None => rev.is_empty(),\n    }\n}","typeGuard":null,"tryCatchPattern":"On 500 'offset is not a transaction boundary', drop the persisted revision and re-pull with an empty client_revision (offset 0), or trigger a fresh bootstrap; never re-derive offsets yourself.","preventionTips":["Store server_revision verbatim from each response; it is always a transaction boundary.","Never compute resume offsets from byte positions.","Do not swap .db-log files between runs."],"tags":["mvcc","sync","revision","logical-log","crc"],"backgroundTag":"invalid-resume-offset","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}