{"record":{"id":"187f21b46867d182","repo":"benbjohnson/litestream","slug":"write-page-d-w-187f21","errorCode":null,"errorMessage":"write page %d: %w","messagePattern":"write page (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":870,"sourceCode":"\t\treturn fmt.Errorf(\"decode header: %w\", err)\n\t}\n\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\n\t// Apply each page to the hydration file\n\tfor {\n\t\tvar phdr ltx.PageHeader\n\t\tdata := make([]byte, h.pageSize)\n\t\tif err := dec.DecodePage(&phdr, data); err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"decode page: %w\", err)\n\t\t}\n\n\t\toff := int64(phdr.Pgno-1) * int64(h.pageSize)\n\t\tif _, err := h.file.WriteAt(data, off); err != nil {\n\t\t\treturn fmt.Errorf(\"write page %d: %w\", phdr.Pgno, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ReadAt reads data from the hydrated local file.\nfunc (h *Hydrator) ReadAt(p []byte, off int64) (int, error) {\n\th.mu.Lock()\n\tn, err := h.file.ReadAt(p, off)\n\th.mu.Unlock()\n\n\tif err != nil && err != io.EOF {\n\t\treturn n, fmt.Errorf(\"read hydrated file: %w\", err)\n\t}\n\n\t// Update the first page to pretend like we are in journal mode\n\tif off == 0 && len(p) >= 28 {","sourceCodeStart":852,"sourceCodeEnd":888,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L852-L888","documentation":"After decoding a page, ApplyLTX writes it to the local hydration file at offset (Pgno-1)*pageSize via WriteAt; failure is wrapped as \"write page %d\" with the page number. This is a local disk I/O failure, not a replication problem — the hydrated database file cannot be updated.","triggerScenarios":"Calling ApplyLTX when the hydration file's volume is full, the file descriptor was closed by another goroutine, the file was truncated/deleted underneath the hydrator, or an OS-level I/O error (EIO, EDQUOT, ENOSPC) occurs.","commonSituations":"Disk quota exceeded on the node; tmpfs/small ephemeral storage for the hydration path; container volume detached mid-run; concurrent `litestream reset` closing the file while ApplyLTX writes.","solutions":["Check disk space and inode/quota on the volume holding the hydration file; free space or move the hydration path to a larger volume.","Verify no concurrent code path closes or truncates h.file (reset/disable) during ApplyLTX; serialize with h.mu or lifecycle checks.","If the file handle is stale after reset, re-create the hydration file (createHydrationFile) and re-run the full Restore.","Inspect dmesg/journal for hardware I/O errors (EIO) and replace failing storage if present."],"exampleFix":"// before\nif _, err := h.file.WriteAt(data, off); err != nil {\n    return fmt.Errorf(\"write page %d: %w\", phdr.Pgno, err)\n}\n// after\nif _, err := h.file.WriteAt(data, off); err != nil {\n    if errors.Is(err, syscall.ENOSPC) {\n        return fmt.Errorf(\"write page %d: disk full at %s: %w\", phdr.Pgno, h.path, err)\n    }\n    return fmt.Errorf(\"write page %d: %w\", phdr.Pgno, err)\n}","handlingStrategy":"try-catch","validationCode":"if st, err := h.file.Stat(); err != nil {\n    return fmt.Errorf(\"hydration file unavailable: %w\", err)\n}\nif free, err := diskFree(filepath.Dir(h.path)); err == nil && free < minFreeBytes {\n    return fmt.Errorf(\"insufficient disk space for hydration\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := h.file.WriteAt(data, off); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) && errors.Is(pe.Err, syscall.ENOSPC) {\n        // free space or fail over to another volume, then re-run restore\n    }\n    return fmt.Errorf(\"write page %d: %w\", phdr.Pgno, err)\n}","preventionTips":["Monitor disk usage/inodes on the hydration volume and alert before full","Do not run `litestream reset` concurrently with ApplyLTX","Place the hydration file on durable local storage, not ephemeral tmpfs"],"tags":["disk","io","file-write-failed"],"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"}