{"record":{"id":"3635077ba23a5f97","repo":"benbjohnson/litestream","slug":"read-page-d-from-buffer-w","errorCode":null,"errorMessage":"read page %d from buffer: %w","messagePattern":"read page (.+?) from buffer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":2158,"sourceCode":"\t\t\tMaxTXID:   pendingTXID,\n\t\t\tTimestamp: time.Now().UnixMilli(),\n\t\t}); err != nil {\n\t\t\terr = fmt.Errorf(\"encode header: %w\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// Encode each dirty page\n\t\tlockPgno := ltx.LockPgno(pageSize)\n\t\tfor _, pgno := range pgnos {\n\t\t\tif pgno == lockPgno {\n\t\t\t\tcontinue // Skip lock page\n\t\t\t}\n\n\t\t\t// Read page data from buffer file\n\t\t\tbufferOff := dirtyOffsets[pgno]\n\t\t\tdata := make([]byte, pageSize)\n\t\t\tif _, err = bufferFile.ReadAt(data, bufferOff); err != nil {\n\t\t\t\terr = fmt.Errorf(\"read page %d from buffer: %w\", pgno, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif err = enc.EncodePage(ltx.PageHeader{Pgno: pgno}, data); err != nil {\n\t\t\t\terr = fmt.Errorf(\"encode page %d: %w\", pgno, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Close encoder (writes trailer and page index)\n\t\tif err = enc.Close(); err != nil {\n\t\t\terr = fmt.Errorf(\"close encoder: %w\", err)\n\t\t\treturn\n\t\t}\n\t}()\n\n\treturn pr\n}","sourceCodeStart":2140,"sourceCodeEnd":2176,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2140-L2176","documentation":"When encoding an LTX transaction, each dirty page's data is read back from the write-buffer file at the offset recorded when the page was written. This error wraps bufferFile.ReadAt failing for a specific page number, meaning the write buffer is unreadable, truncated, or the recorded offset is beyond the file's end.","triggerScenarios":"bufferFile.ReadAt(data, bufferOff) returns an error during sync: buffer file deleted/truncated underneath the VFS, offset from f.dirty exceeding buffer length (buffer reset without clearing dirty map), or underlying I/O error.","commonSituations":"External cleanup jobs removing the buffer file; crash between buffer truncation and dirty-map reset; memory/disk corruption on tmp volumes; mixing processes against the same buffer file path.","solutions":["Verify the buffer file still exists and its size matches expectations; restore it by restarting the process (buffer is rebuilt on open).","Ensure no external process truncates or rotates the buffer file path (f.bufferPath).","Check the wrapped error: EOF/ErrUnexpectedEOF implies dirty-offset/bookkeeping drift — restart the database handle; EIO implies failing disk.","Keep the database and its VFS state confined to a single process; do not share the buffer path across instances."],"exampleFix":"// before: logrotate matching *.buffer truncates live buffer files\n// /etc/logrotate.d/app: /var/tmp/*.buffer { copytruncate }\n// after: exclude litestream buffer files from rotation\n// /var/tmp/*.log { copytruncate }","handlingStrategy":"try-catch","validationCode":"// confirm buffer file exists and is not truncated externally\nif fi, err := os.Stat(bufferPath); err != nil || fi.Size() == 0 { return errors.New(\"write buffer missing/empty; restart required\") }","typeGuard":null,"tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n    if strings.Contains(err.Error(), \"read page\") {\n        logger.Error(\"write buffer unreadable; reopen DB to rebuild buffer\", \"err\", err)\n    }\n    return err\n}","preventionTips":["Exclude buffer files from rotation/cleanup tooling","One process per buffer path — never share across instances","Run fs-health checks if buffer read errors recur"],"tags":["go","vfs","write-buffer","file-io"],"backgroundTag":"file-read-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}