{"record":{"id":"c59c74160f3c0d02","repo":"benbjohnson/litestream","slug":"write-to-buffer-w","errorCode":null,"errorMessage":"write to buffer: %w","messagePattern":"write to buffer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"vfs.go","lineNumber":1686,"sourceCode":"\t\t// Page is not dirty - read from cache/remote\n\t\tif err := f.readPageForWrite(pgno, page); err != nil {\n\t\t\t// If page doesn't exist, use zero-filled page\n\t\t\tf.logger.Debug(\"page not found, using empty page\", \"pgno\", pgno)\n\t\t}\n\t}\n\n\t// Apply write to page\n\tn = copy(page[pageOffset:], b)\n\n\t// Update commit count if this extends the database\n\tif pgno > f.commit {\n\t\tf.commit = pgno\n\t}\n\n\t// Write to buffer for durability (this updates f.dirty with the offset)\n\tif err := f.writeToBuffer(pgno, page); err != nil {\n\t\tf.logger.Error(\"failed to write to buffer\", \"error\", err)\n\t\treturn 0, fmt.Errorf(\"write to buffer: %w\", err)\n\t}\n\n\tf.logger.Debug(\"wrote to dirty page\", \"pgno\", pgno, \"offset\", pageOffset, \"len\", n, \"commit\", f.commit)\n\treturn n, nil\n}\n\n// readPageForWrite reads a page into buf for modification.\n// Must be called with f.mu held.\nfunc (f *VFSFile) readPageForWrite(pgno uint32, buf []byte) error {\n\tpageSize := uint32(len(buf))\n\n\t// Check cache first (cache is thread-safe, but we hold the lock anyway)\n\tif data, ok := f.cache.Get(pgno); ok {\n\t\tcopy(buf, data)\n\t\treturn nil\n\t}\n\n\t// Get page index element","sourceCodeStart":1668,"sourceCodeEnd":1704,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L1668-L1704","documentation":"VFSFile.WriteAt failed while appending the modified page into the local durable write buffer file (the tmpfs-backed file holding dirty pages before they are synced to the replica as an LTX file). The wrapped error comes from writeToBuffer, typically a disk I/O failure on the buffer file (write, grow, or flush). Since this is a disaster-recovery path, the write is aborted so the page is not reported as persisted.","triggerScenarios":"Calling SQLite writes (via the sqlite3vfs VFS) when writeEnabled is true and writeToBuffer cannot write the page into the buffer file: disk full, buffer file closed/never initialized, or I/O error on the temp filesystem. Also triggered when the buffer file handle is stale after a sync cleared the buffer.","commonSituations":"Temp directory (WriteBufferPath or os.TempDir) on a full or read-only disk; container with a small tmpfs that fills with large transactions; buffer file deleted out from under a running process (e.g. tmp cleaner); calling SetWriteEnabled(true) on a VFSFile constructed without a proper buffer path.","solutions":["Check free space and writability of the write-buffer path (WriteBufferPath or os.TempDir()); free space or point WriteBufferPath at a larger volume.","Ensure SetWriteEnabled(true) succeeded before writing — the buffer file must be initialized (initWriteBufferWithLock).","Restart/reopen the VFS file if the buffer file handle was closed externally (e.g. tmpwatch deleted it).","Inspect the wrapped error in logs ('failed to write to buffer') for the exact syscall failure.","Retry the transaction after fixing disk conditions; dirty state is unchanged on failure."],"exampleFix":"// before\nvfs, _ := litestreamvfs.New(..., litestreamvfs.WithWriteEnabled(true)) // temp dir on small tmpfs\n// after\ncfg.VFS.WriteBufferPath = \"/var/lib/litestream/write-buffer\" // persistent volume with space\nif err := file.SetWriteEnabled(true); err != nil { log.Fatal(err) }","handlingStrategy":"try-catch","validationCode":"// Go\nfunc canWriteBuffer(path string) error {\n    fi, err := os.Stat(filepath.Dir(path))\n    if err != nil { return err }\n    if !fi.IsDir() { return fmt.Errorf(\"not a dir\") }\n    f, err := os.CreateTemp(filepath.Dir(path), \".probe\")\n    if err != nil { return err }\n    f.Close(); os.Remove(f.Name())\n    return nil\n}","typeGuard":null,"tryCatchPattern":"n, err := file.WriteAt(buf, off)\nif err != nil {\n    var werr *fs.PathError\n    if errors.As(err, &werr) { log.Errorf(\"buffer I/O: %v\", werr.Err) }\n    // surface to SQLite as IOERR; check disk space before retry\n}","preventionTips":["Provision the write-buffer volume with headroom for your largest transaction.","Set WriteBufferPath explicitly to a persistent writable path instead of TMPDIR.","Monitor disk free space on the buffer volume.","Exclude the buffer path from tmp-cleaner/tmpwatch jobs."],"tags":["go","storage","write-buffer","disk-full","sqlite-vfs"],"backgroundTag":"file-write-failed","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"}