{"record":{"id":"679cfe77717b372e","repo":"juicedata/juicefs","slug":"merge-sorted-records-s","errorCode":null,"errorMessage":"merge sorted records: %s","messagePattern":"merge sorted records: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/gc_external.go","lineNumber":79,"sourceCode":"\t\tif err := scanGcMetaRecords(sortMetaCtx, m, metaSorter.Input(), stats.slices); err != nil {\n\t\t\treturn errors.Errorf(\"produce meta records: %s\", err)\n\t\t}\n\t\tmetaSorter.CloseInput()\n\t\treturn nil\n\t})\n\n\teg.Go(func() error {\n\t\tdefer stats.scanned.Done()\n\t\tif err := scanGcObjectRecords(sortCtx, blobWithPrefix, objSorter.Input(), threads, chunkConf.HashPrefix, maxMtime, stats.prefixes, stats.scanned, stats.skipped); err != nil {\n\t\t\treturn errors.Errorf(\"produce object records: %s\", err)\n\t\t}\n\t\tobjSorter.CloseInput()\n\t\treturn nil\n\t})\n\n\teg.Go(func() error {\n\t\tif err := mergeGcSortedRecords(sortCtx, metaSorter, objSorter, chunkConf.BlockSize, stats, leakedObj); err != nil {\n\t\t\treturn errors.Errorf(\"merge sorted records: %s\", err)\n\t\t}\n\t\treturn nil\n\t})\n\n\tsortErr := eg.Wait()\n\t_ = metaSorter.Done()\n\t_ = objSorter.Done()\n\twaitLeakedObj()\n\tif sortErr != nil {\n\t\treturn sortErr\n\t}\n\treturn nil\n}\n\nfunc newGcExternalSorters(ctx context.Context, extSortDir string, threads int) (*extsort.Sorter[gcMetaRecord], *extsort.Sorter[gcObjectRecord], error) {\n\tmetaSorter, err := extsort.New(ctx, extsort.Config{\n\t\tWorkDir:  extSortDir,\n\t\tName:     \"gc-meta\",","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/cmd/gc_external.go#L61-L97","documentation":"Wrapped error from the errgroup worker running mergeGcSortedRecords, which merge-scans the externally sorted meta and object records to classify each block as used/trash/pending/leaked. A failure here (sorter read error, corrupted spill file, codec/checksum mismatch, ctx cancellation) aborts the GC before any leaked object is deleted.","triggerScenarios":"`juicefs gc --ext-sort` during the merge phase: disk holding extSortDir filled or spill files removed, checksum mismatch on a sorter chunk, sorter iterator error, or sortCtx cancelled because the meta/object producer goroutines failed first.","commonSituations":"ext-sort directory on a small tmpfs running out of space; admin deleting temp files from extSortDir mid-run; disk I/O errors; another goroutine's failure cancelling the shared context.","solutions":["Check free space on the ext-sort directory (`df -h <dir>`) and enlarge or relocate it.","Re-run GC after producers succeed — this error often cascades from the meta/object scan errors.","Point --ext-sort at a reliable local disk, not tmpfs or an NFS mount.","Verify no other process removes files from the ext-sort dir during the run.","Check the embedded cause for checksum/codec errors; if present, retry once (transient I/O) then report a bug with logs."],"exampleFix":"// before: tmpfs too small\njuicefs gc --ext-sort /tmp/run $META\n// after\nmkdir -p /var/jfs-gcsort && juicefs gc --ext-sort /var/jfs-gcsort $META","handlingStrategy":"validation","validationCode":"DIR=$EXT_SORT_DIR; df --output=avail -B1G \"$DIR\" | tail -1 | awk '{exit ($1>1024)?0:1}' || { echo 'ext-sort dir needs >1GB free'; exit 1; }","typeGuard":null,"tryCatchPattern":"if strings.Contains(out, \"merge sorted records:\") {\n    // check for cascade from producer errors, disk space, or checksum errors; re-run\n}","preventionTips":["Provision ample free space on the ext-sort dir (local disk, not tmpfs/NFS)","Protect the temp dir from external cleanup jobs","Address producer (meta/object) errors before re-running"],"tags":["go","gc","external-sort","disk-space","errgroup"],"backgroundTag":"file-write-failed","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}