{"record":{"id":"ea766773301ba973","repo":"charmbracelet/crush","slug":"failed-to-write-provider-data-to-cache-w","errorCode":null,"errorMessage":"failed to write provider data to cache: %w","messagePattern":"failed to write provider data to cache: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/provider.go","lineNumber":300,"sourceCode":"}\n\nfunc (c cache[T]) Store(v T) error {\n\tslog.Info(\"Saving provider data to disk\", \"path\", c.path)\n\tif err := os.MkdirAll(filepath.Dir(c.path), 0o755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create directory for provider cache: %w\", err)\n\t}\n\n\tdata, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal provider data: %w\", err)\n\t}\n\n\t// Written through a temporary file and renamed into place. Several Crush\n\t// instances start independently and race to refresh this cache, and a\n\t// truncating write would let one of them read a half-written catalog and\n\t// silently fall back to the bundled copy.\n\tif err := atomicWriteFile(c.path, data, 0o644); err != nil {\n\t\treturn fmt.Errorf(\"failed to write provider data to cache: %w\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":282,"sourceCodeEnd":304,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/provider.go#L282-L304","documentation":"Thrown by cache.Store() when the atomic write (temp file + rename via atomicWriteFile) fails to persist the marshaled provider data. The atomic design protects concurrent Crush instances from reading a half-written cache, but the final write/rename can still fail at the OS level.","triggerScenarios":"atomicWriteFile(c.path, data, 0o644) fails: disk full (ENOSPC), permission denied on the target directory or existing file, the temp file cannot be created/renamed (cross-device), or the filesystem is read-only.","commonSituations":"Quota exceeded on the cache volume; read-only filesystem after mount remount; concurrent writers on a filesystem where rename semantics differ; SELinux/AppArmor denial.","solutions":["Free disk space / check quota (df -h) on the cache volume.","Fix permissions on the cache directory and existing cache file.","Ensure the path and its temp file are on the same filesystem so rename succeeds.","Check for mandatory access control denials (audit logs) and adjust policy.","Retry the update once the filesystem issue is resolved; the previous cache remains intact."],"exampleFix":"// before\nStore(providers) // ENOSPC\n// after\ndf -h ~/.cache/crush  # free space, then retry\ncrush update-providers","handlingStrategy":"retry","validationCode":"dir := filepath.Dir(cachePath)\nif err := unix.Access(dir, unix.W_OK); err != nil { /* warn: unwritable */ }\n// also check free space periodically","typeGuard":null,"tryCatchPattern":"err := cache.Store(v)\nfor i := 0; err != nil && i < 3; i++ {\n    time.Sleep(100 * time.Millisecond)\n    err = cache.Store(v)\n}","preventionTips":["Monitor disk space/quota on the cache volume","Keep cache dir writable and on the same filesystem as its temp files","Don't remount read-only while Crush runs","Old cache survives failed writes — retry safely"],"tags":["filesystem","cache","atomic-write"],"backgroundTag":"file-write-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}