{"record":{"id":"c4bff3e38d534f58","repo":"benbjohnson/litestream","slug":"encode-header-w","errorCode":null,"errorMessage":"encode header: %w","messagePattern":"encode header: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":2143,"sourceCode":"\t\t}()\n\n\t\tenc, encErr := ltx.NewEncoder(pw)\n\t\tif encErr != nil {\n\t\t\terr = encErr\n\t\t\treturn\n\t\t}\n\n\t\t// Encode header\n\t\tif err = enc.EncodeHeader(ltx.Header{\n\t\t\tVersion:   ltx.Version,\n\t\t\tFlags:     ltx.HeaderFlagNoChecksum,\n\t\t\tPageSize:  pageSize,\n\t\t\tCommit:    commit,\n\t\t\tMinTXID:   pendingTXID,\n\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","sourceCodeStart":2125,"sourceCodeEnd":2161,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2125-L2161","documentation":"While building an LTX file during sync, the encoder's EncodeHeader call failed and the error is wrapped as \"encode header\". The LTX header (page size, commit, min/max TXID, timestamp) is the first record in the immutable LTX file; if it cannot be encoded/written the transaction file is invalid and sync for this transaction fails.","triggerScenarios":"enc.EncodeHeader(ltx.Header{...}) returns an error while streaming dirty pages into a new LTX file — typically the underlying writer (pipe or file) is broken, closed early, or hit a disk error mid-write.","commonSituations":"Reader side of the streaming pipe exited early (e.g. the replica client Upload failed or was cancelled), breaking the pipe with ErrClosedPipe; disk full on the staging volume; context cancellation during a slow upload.","solutions":["Check the wrapped cause: a broken-pipe error usually means the replica upload failed — fix the storage/network issue and retry the sync.","Verify disk space on the volume staging the LTX file.","Avoid cancelling the sync context mid-upload (shutdown timeouts too short); increase graceful-shutdown timeout.","If reproducible with no I/O cause, capture the exact wrapped error and file an upstream issue; LTX files are immutable so a partially written file must be discarded."],"exampleFix":"// before: svc shuts down with 1s timeout, killing uploads mid-header\n// after: allow in-flight syncs to finish\nsrv.Shutdown(ctx) // ensure ctx has >= 30s grace so LTX encode/upload completes","handlingStrategy":"try-catch","validationCode":"// ensure staging volume has headroom before large syncs\nif st, err := os.Statvfs(stagingDir); err == nil && st.Bavail*uint64(st.Bsize) < minFreeBytes { return errors.New(\"staging disk low\") }","typeGuard":null,"tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n    if strings.Contains(err.Error(), \"encode header\") && errors.Is(err, os.ErrClosed) || strings.Contains(err.Error(), \"pipe\") {\n        // upload side failed; check replica connectivity and retry\n    }\n    return err\n}","preventionTips":["Give shutdown handlers enough grace to finish in-flight uploads","Free-space monitoring on the LTX staging volume","Don't cancel sync contexts mid-transaction"],"tags":["go","ltx","encoding","write-path"],"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-14T00:17:10.932Z"}