{"record":{"id":"6e7d7ad294b966cf","repo":"benbjohnson/litestream","slug":"walreader-readframe-buffer-size-d-must-match","errorCode":null,"errorMessage":"WALReader.ReadFrame(): buffer size (%d) must match page size (%d)","messagePattern":"WALReader\\.ReadFrame\\(\\): buffer size \\((.+?)\\) must match page size \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"wal_reader.go","lineNumber":149,"sourceCode":"\n\treturn nil\n}\n\n// ReadFrame reads the next frame from the WAL and returns the page number.\n// Returns io.EOF at the end of the valid WAL.\nfunc (r *WALReader) ReadFrame(ctx context.Context, data []byte) (pgno, commit uint32, err error) {\n\treturn r.readFrame(ctx, data, true)\n}\n\nfunc (r *WALReader) readFrame(ctx context.Context, data []byte, verifyChecksum bool) (pgno, commit uint32, err error) {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn 0, 0, context.Cause(ctx)\n\tdefault:\n\t}\n\n\tif len(data) != int(r.pageSize) {\n\t\treturn 0, 0, fmt.Errorf(\"WALReader.ReadFrame(): buffer size (%d) must match page size (%d)\", len(data), r.pageSize)\n\t}\n\n\tframeSize := r.pageSize + WALFrameHeaderSize\n\toffset := WALHeaderSize + (int64(r.frameN) * int64(frameSize))\n\n\t// Read WAL frame header.\n\thdr := make([]byte, WALFrameHeaderSize)\n\tif n, err := r.r.ReadAt(hdr, offset); n != len(hdr) {\n\t\treturn 0, 0, io.EOF\n\t} else if err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\t// Read WAL page data.\n\tif n, err := r.r.ReadAt(data, offset+WALFrameHeaderSize); n != len(data) {\n\t\treturn 0, 0, io.EOF\n\t} else if err != nil {\n\t\treturn 0, 0, err","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/wal_reader.go#L131-L167","documentation":"ReadFrame/readFrame fill the caller-supplied buffer with one page per frame. The buffer must be exactly pageSize bytes (as declared in the WAL header); this error is thrown when len(data) differs from the WAL's page size, so the frame read would overflow or underfill the buffer.","triggerScenarios":"Calling ReadFrame with a buffer sized from a stale or assumed page size while the WAL header declares a different one; using the default 4096-byte buffer against a database with page_size=8192 etc.","commonSituations":"Database page size changed via 'PRAGMA page_size' or VACUUM between replication runs; hardcoding 4096 in code that reads frames; mixing readers across databases with different page sizes.","solutions":["Query the actual page size (PRAGMA page_size) and allocate the read buffer from it, not a constant","Obtain the page size from the WALReader (r.pageSize / pageSize field) after NewWALReader* returns, then size the buffer","If the page size changed mid-stream, re-open the WAL reader so the new header/page size is picked up","Never reuse a buffer sized for a previous page size across readers"],"exampleFix":"// before\ndata := make([]byte, 4096)\nn0, n1, err := reader.ReadFrame(ctx, data)\n// after\ndata := make([]byte, reader.PageSize())\nn0, n1, err := reader.ReadFrame(ctx, data)","handlingStrategy":"validation","validationCode":"// derive the buffer from the reader, never a constant\nbuf := make([]byte, r.PageSize())\nif len(buf) != int(r.PageSize()) { panic(\"page size mismatch\") }","typeGuard":null,"tryCatchPattern":"_, _, err := reader.ReadFrame(ctx, data)\nif err != nil && strings.Contains(err.Error(), \"must match page size\") {\n    data = make([]byte, reader.PageSize()) // re-size and retry\n    _, _, err = reader.ReadFrame(ctx, data)\n}","preventionTips":["Always size frame buffers from the WAL reader's reported page size","Re-read PRAGMA page_size after VACUUM or migration; never hardcode 4096","Do not share buffers across databases with different page sizes","Reopen the reader if the database's page size may have changed mid-stream"],"tags":["sqlite","wal","buffer","page-size"],"backgroundTag":"invalid-argument-value","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}