{"record":{"id":"91079847fe050d61","repo":"benbjohnson/litestream","slug":"stage-close","errorCode":"stage-close","errorMessage":"%w: %w (ErrDiskFull)","messagePattern":"%w: %w \\(ErrDiskFull\\)","errorType":"error_code","errorClass":"LTXError","httpStatus":null,"severity":"critical","filePath":"db.go","lineNumber":2222,"sourceCode":"\t\t\treturn result, NewLTXError(\"stage-write\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"close ltx encoder: %w\", err)\n\t}\n\n\t// Sync & close LTX file.\n\tdb.setSyncDiagPhase(diagPhaseFsyncLTX, func(s *diagState) {\n\t\ts.txID = txID\n\t\ts.walSize = sz\n\t})\n\tif err := ltxFile.Sync(); err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-sync\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"sync ltx file: %w\", err)\n\t}\n\tif err := ltxFile.Close(); err != nil {\n\t\tif isDiskFullError(err) {\n\t\t\treturn result, NewLTXError(\"stage-close\", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf(\"%w: %w\", ErrDiskFull, err))\n\t\t}\n\t\treturn result, fmt.Errorf(\"close ltx file: %w\", err)\n\t}\n\n\t// Atomically rename file to final path.\n\tdb.setSyncDiagPhase(diagPhaseRenameLTX, func(s *diagState) {\n\t\ts.txID = txID\n\t\ts.walSize = sz\n\t})\n\tif err := os.Rename(tmpFilename, filename); err != nil {\n\t\tdb.maxLTXFileInfos.Lock()\n\t\tdelete(db.maxLTXFileInfos.m, 0) // clear cache if in unknown state\n\t\tdb.maxLTXFileInfos.Unlock()\n\t\tdb.invalidatePosCache()\n\t\treturn result, fmt.Errorf(\"rename ltx file: %w\", err)\n\t}\n\tif err := internal.FsyncDir(filepath.Dir(filename)); err != nil {\n\t\tdb.maxLTXFileInfos.Lock()","sourceCodeStart":2204,"sourceCodeEnd":2240,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2204-L2240","documentation":"After fsync succeeded, litestream closes the LTX file; a close error classified as disk-full is reported as ErrDiskFull with stage 'stage-close'. On many filesystems close() performs the final flush of buffered data, so a full disk can surface here even though Sync() already returned success.","triggerScenarios":"ltxFile.Close() at db.go:2220 returned ENOSPC/EDQUOT or a message matched by isDiskFullError. Typical when the disk filled between the fsync and the close, or on filesystems where close flushes remaining metadata/buffers.","commonSituations":"Rapid concurrent writes filling the volume in the milliseconds between sync and close; quota enforcement on close-time flush; small container overlay filesystems hitting capacity.","solutions":["Free disk space or raise the quota on the data volume.","Check whether other processes (logs, compaction temps) are consuming space concurrently and throttle them.","Verify the LTX path has enough headroom for the max snapshot size before starting a snapshot.","Retry the sync after cleanup; the failed temp file is discarded."],"exampleFix":"// before\n// ignoring disk state before snapshots\nres, err := db.Sync(ctx)\n// after\nif freeBytes(path) < minSnapshotHeadroom {\n\treturn ErrDiskFull // pre-flight check\n}\nres, err := db.Sync(ctx)\nif errors.Is(err, ErrDiskFull) { freeSpaceOrAlert() }","handlingStrategy":"try-catch","validationCode":"if freeBytes(ltxDir) < maxSnapshotSize { return errors.New(\"insufficient headroom\") }","typeGuard":"func isDiskFull(err error) bool { return errors.Is(err, ErrDiskFull) }","tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n\tif errors.Is(err, ErrDiskFull) {\n\t\t// handle close-time ENOSPC: free space, retry\n\t}\n\treturn err\n}","preventionTips":["Reserve headroom above peak DB+WAL size on the data volume.","Throttle co-located write-heavy workloads.","Alert on disk usage trends, not just 100% full.","Prefer volumes without tight per-user quotas for the data dir."],"tags":["disk-full","close","filesystem","storage"],"backgroundTag":"disk-full","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"}