benbjohnson/litestream · error

page size mismatch: got %d, expected %d

Error message

page size mismatch: got %d, expected %d

What it means

FetchPage returned a page whose byte length differs from the VFS file's configured page size (f.pageSizeBytes / len(buf)). Litestream assumes every LTX page image matches the database page size; a mismatch means the replica data and the local page size disagree, so copying it would corrupt the page buffer.

Source

Thrown at vfs.go:1717

	if data, ok := f.cache.Get(pgno); ok {
		copy(buf, data)
		return nil
	}

	// Get page index element
	elem, ok := f.index[pgno]
	if !ok {
		return fmt.Errorf("page not found: %d", pgno)
	}

	// Fetch from remote
	_, data, err := FetchPage(f.ctx, f.client, elem.Level, elem.MinTXID, elem.MaxTXID, elem.Offset, elem.Size)
	if err != nil {
		return err
	}

	if uint32(len(data)) != pageSize {
		return fmt.Errorf("page size mismatch: got %d, expected %d", len(data), pageSize)
	}

	copy(buf, data)
	f.cache.Add(pgno, data)
	return nil
}

func (f *VFSFile) Truncate(size int64) error {
	f.logger.Debug("truncating file", "size", size)

	pageSize, err := f.pageSizeBytes()
	if err != nil {
		return err
	}

	newCommit := uint32(size / int64(pageSize))

	f.mu.Lock()

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Verify the remote database snapshot's page size matches the local VFS database page size; do not share replicas across differently-configured databases.
  2. Inspect the LTX file at the indexed offset (litestream ltx -level 0) for truncation/corruption.
  3. Re-replicate: run litestream reset for the database so a fresh snapshot with the correct page size is created.
  4. Check for interrupted uploads/partial writes at the storage backend and re-upload affected LTX files.
  5. Ensure the client app doesn't change PRAGMA page_size on an existing replicated database.
Defensive patterns

Strategy: validation

Validate before calling

// Verify snapshot page size matches local DB before pointing VFS at a replica:
// read bytes 16-17 of the DB header (page size) and compare with remote snapshot config.
localPS := binary.BigEndian.Uint16(header[16:18]) // must equal replica page size

Type guard

func pageSizeOK(data []byte, want uint32) bool { return uint32(len(data)) == want }

Try / catch

if err := readPageForWrite(pgno, buf); err != nil {
    if strings.Contains(err.Error(), "page size mismatch") {
        // stop writes; re-replicate with matching page size
    }
}

Prevention

When it happens

Trigger: Calling WriteAt (read-modify-write path) on a page present in f.index but whose stored LTX record was written with a different page size — e.g. the remote snapshot was created from a database with a different page_size, or a truncated/corrupt LTX record yields a short read.

Common situations: Pointing a VFS at a replica from a different database with a different PRAGMA page_size; restoring across litestream versions where page-size handling changed; corrupted or truncated LTX files on storage (partial upload); manually copied replica data between databases.

Understand the failure class

Background: Tensor shape mismatch errors ("must have shape", "expected shape ... got ..."): when tensor dimensions disagree with what an op or layer was told to expect — this error's family across 6 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/13289687cb85d2fb. Report an issue: GitHub.