{"record":{"id":"cf13a0887b658372","repo":"benbjohnson/litestream","slug":"stage-sync","errorCode":"stage-sync","errorMessage":"%w: %w (ErrDiskFull)","messagePattern":"%w: %w \\(ErrDiskFull\\)","errorType":"error_code","errorClass":"LTXError","httpStatus":null,"severity":"critical","filePath":"db.go","lineNumber":2216,"sourceCode":"\tdb.setSyncDiagPhase(diagPhaseCloseLTX, func(s *diagState) {\n\t\ts.txID = txID\n\t\ts.walSize = sz\n\t})\n\tif err := enc.Close(); err != nil {\n\t\tif isDiskFullError(err) {\n\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","sourceCodeStart":2198,"sourceCodeEnd":2234,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2198-L2234","documentation":"During a WAL-to-LTX sync, litestream fsyncs the temporary LTX file before renaming it into place. If the fsync fails and the OS error indicates the filesystem is out of space, the error is wrapped as ErrDiskFull with the structured LTX stage marker 'stage-sync'. The library classifies this specifically so callers can react to disk exhaustion rather than treating it as a generic I/O failure.","triggerScenarios":"The DB sync loop wrote the LTX file but ltxFile.Sync() returned ENOSPC/EDQUOT (or a message matched by isDiskFullError) at db.go:2214-2216. Happens on the periodic sync after WAL frames are appended while the disk filled up mid-sync.","commonSituations":"Small disk volumes with heavy write load; disk quota (EDQUOT) on shared hosts or containers with a volume size limit; logs or WAL growth consuming the last free blocks; snapshotting a large database where the temp LTX file plus WAL exceed free space.","solutions":["Free disk space on the volume holding the database/LTX path (delete old snapshots, logs, or truncate the WAL via checkpointing).","Check filesystem quota with df -h and quota commands; raise the quota or move the data directory to a larger volume.","Enable the replica's auto-recover option or run `litestream reset` if local LTX state is inconsistent after the failure.","Configure disk usage alerts so space is reclaimed before ENOSPC; consider a lifecycle policy for retained LTX files.","Retry the sync after space is freed; the operation is idempotent since the temp file is discarded on error."],"exampleFix":"// before\nif err := db.Sync(ctx); err != nil {\n\tlog.Fatal(err) // opaque failure\n}\n// after\nif err := db.Sync(ctx); err != nil {\n\tif errors.Is(err, ErrDiskFull) {\n\t\tfreeSpaceOrAlert() // handle ENOSPC specifically\n\t}\n\treturn err\n}","handlingStrategy":"try-catch","validationCode":"const free = require('check-disk-space')\n// before heavy writes / periodic check\nif (freeBytes(dbPath) < 2 * maxExpectedWALSize) {\n  alertOrCleanUp() // avoid ENOSPC mid-sync\n}","typeGuard":"func isDiskFull(err error) bool {\n\treturn errors.Is(err, ErrDiskFull)\n}","tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n\tif errors.Is(err, ErrDiskFull) {\n\t\t// free space / alert / retry after cleanup\n\t}\n\treturn err\n}","preventionTips":["Monitor free disk space and alert well before capacity.","Set cloud lifecycle policies or retention so LTX files don't accumulate.","Size volumes for max WAL growth plus snapshot headroom.","Watch for EDQUOT: check quotas, not just df, on shared hosts."],"tags":["disk-full","filesystem","fsync","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"}