tursodatabase/turso · error

Failed to parse server_pages_selector: {}

Error message

Failed to parse server_pages_selector: {}

What it means

server_pages_selector (protobuf tag 5, bytes) carries a RoaringBitmap listing the page ids the client still needs; empty bytes mean 'send all pages'. When the field is non-empty but RoaringBitmap::deserialize_from rejects it, the pull request fails with HTTP 500 before any page is read.

Source

Thrown at cli/sync_server.rs:541

        } else {
            req.server_revision.parse().unwrap_or(wal_state.max_frame)
        };

        let client_revision: u64 = if req.client_revision.is_empty() {
            0
        } else {
            req.client_revision.parse().unwrap_or(0)
        };

        debug!(
            "Using server_revision={}, client_revision={}",
            server_revision, client_revision
        );

        let pages_selector: Option<RoaringBitmap> = if !req.server_pages_selector.is_empty() {
            Some(
                RoaringBitmap::deserialize_from(&req.server_pages_selector[..])
                    .map_err(|e| anyhow!("Failed to parse server_pages_selector: {}", e))?,
            )
        } else {
            None
        };

        let mut seen_pages: HashSet<u32> = HashSet::new();
        let mut pages_to_send: Vec<(u32, Vec<u8>)> = Vec::new();

        let frame_size = WAL_FRAME_HEADER_SIZE + PAGE_SIZE;
        let mut frame_buffer = vec![0u8; frame_size];

        debug!(
            "pull-updates: scanning WAL frames {}..={} (client_revision={}, server_revision={})",
            client_revision + 1,
            server_revision,
            client_revision,
            server_revision
        );

View on GitHub (pinned to bad083fafb)

Solutions

  1. Build the selector with the same roaring crate the server uses: collect ids into RoaringBitmap and serialize_into a buffer.
  2. Round-trip check locally (serialize then deserialize_from) before sending the request.
  3. Omit the field (empty bytes) to request all pages when chunking is not needed.
  4. Verify cached selector blobs are byte-identical to what the client's roaring crate emits on its platform.

Example fix

// before
req.server_pages_selector = Bytes::from(raw_bitset_bytes);

// after
use roaring::RoaringBitmap;
let bitmap: RoaringBitmap = missing_pages.into_iter().collect();
let mut buf = Vec::new();
bitmap.serialize_into(&mut buf)?;
req.server_pages_selector = Bytes::from(buf);
Defensive patterns

Strategy: validation

Validate before calling

if !req.server_pages_selector.is_empty() {
    roaring::RoaringBitmap::deserialize_from(&req.server_pages_selector[..])
        .map_err(|e| format!("selector blob is not a valid RoaringBitmap: {e}"))?;
}

Try / catch

On 500 'Failed to parse server_pages_selector', drop the selector (send empty bytes = all pages) or rebuild the bitmap and retry; never resend the same blob.

Prevention

When it happens

Trigger: Sending a base64-decoded but wrong blob; a bitmap serialized by a different Roaring implementation or serialization variant; truncated bytes; page ids outside the u32 domain of the bitmap.

Common situations: Chunked bootstrap code that caches selector blobs between sessions; interop between roaring crate versions; hand-assembled pull requests in tests.

Understand the failure class

Related errors


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