{"record":{"id":"abd04caf73d86afc","repo":"gastownhall/beads","slug":"failed-to-close-temp-file-w","errorCode":null,"errorMessage":"failed to close temp file: %w","messagePattern":"failed to close temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/backup_export.go","lineNumber":116,"sourceCode":"\ttmp, err := os.CreateTemp(dir, \".backup-tmp-*\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create temp file: %w\", err)\n\t}\n\ttmpPath := tmp.Name()\n\n\tif _, err := tmp.Write(data); err != nil {\n\t\t_ = tmp.Close()\n\t\t_ = os.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to write temp file: %w\", err)\n\t}\n\tif err := tmp.Sync(); err != nil {\n\t\t_ = tmp.Close()\n\t\t_ = os.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to sync temp file: %w\", err)\n\t}\n\tif err := tmp.Close(); err != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to close temp file: %w\", err)\n\t}\n\tif err := os.Rename(tmpPath, path); err != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to rename temp file: %w\", err)\n\t}\n\treturn nil\n}\n\n// runBackupExport performs a Dolt-native backup to .beads/backup/.\n// Returns the updated state.\nfunc runBackupExport(ctx context.Context, force bool) (*backupState, error) {\n\tdir, err := backupDir()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstate, err := loadBackupState(dir)\n\tif err != nil {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/backup_export.go#L98-L134","documentation":"atomicWriteFile explicitly closes the temp file after syncing; this wraps a tmp.Close failure. A failed close means buffered data may not have reached disk reliably, so the temp file is discarded and the destination untouched.","triggerScenarios":"tmp.Close() returns non-nil: file descriptor exhaustion (EMFILE), or a deferred write error surfaced at close.","commonSituations":"Process hit its open-file limit (ulimit -n) due to leaked descriptors; kernel memory pressure preventing flush.","solutions":["Check open file descriptor limits and usage: ulimit -n; lsof | wc -l.","Raise the limit if exhausted (ulimit -n 4096 or systemd LimitNOFILE) or fix leaking processes.","Retry the command — atomic write guarantees the previous backup_state.json is intact.","If persistent, check for system-wide file table exhaustion (fs.file-max)."],"exampleFix":"// shell\nulimit -n 4096 && bd sync","handlingStrategy":"validation","validationCode":"// fd exhaustion is the usual cause; check headroom before running\ncount, _ := filepath.Glob(\"/proc/self/fd/*\")\nif len(count) > 900 { // typical soft limit 1024\n    return errors.New(\"too many open fds before bd operation\")\n}","typeGuard":null,"tryCatchPattern":"if err := saveBackupState(dir, state); err != nil {\n    if strings.Contains(err.Error(), \"close temp file\") {\n        log.Printf(\"fd pressure suspected; raise ulimit -n and retry: %v\", err)\n        return err\n    }\n    return err\n}","preventionTips":["Raise NOFILE limits in CI/systemd (LimitNOFILE=4096)","Fix descriptor leaks in long-running wrapper processes","Avoid nesting many bd invocations concurrently in one shell environment"],"tags":["go","filesystem","file-descriptor","atomic-write"],"backgroundTag":"too-many-open-files","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}