{"record":{"id":"4cecaa4adb43e377","repo":"vxcontrol/pentagi","slug":"blob-for-resource-q-not-found-on-disk","errorCode":null,"errorMessage":"blob for resource %q not found on disk","messagePattern":"blob for resource %q not found on disk","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"backend/pkg/server/services/resources.go","lineNumber":1882,"sourceCode":"\t\t\t\tresponse.Error(c, response.ErrResourcesNotFound, fmt.Errorf(\"resource %q not found\", targetPath))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlogger.FromContext(c).WithError(err).Error(\"error finding resource for download\")\n\t\t\tresponse.Error(c, response.ErrInternal, err)\n\t\t\treturn\n\t\t}\n\t\tentries = append(entries, resolvedEntry{rec: rec})\n\t}\n\n\t// Single regular file → serve as a direct attachment with explicit Content-Length.\n\tif len(entries) == 1 && !entries[0].rec.IsDir {\n\t\te := entries[0]\n\t\tblobPath := resources.BlobPath(s.dataDir, e.rec.Hash)\n\t\tf, err := os.Open(blobPath)\n\t\tif err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tresponse.Error(c, response.ErrResourcesNotFound,\n\t\t\t\t\tfmt.Errorf(\"blob for resource %q not found on disk\", e.rec.Path))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlogger.FromContext(c).WithError(err).Error(\"error opening resource blob for download\")\n\t\t\tresponse.Error(c, response.ErrInternal, err)\n\t\t\treturn\n\t\t}\n\t\tdefer f.Close()\n\n\t\tinfo, err := f.Stat()\n\t\tif err != nil {\n\t\t\tlogger.FromContext(c).WithError(err).Error(\"error stating resource blob for download\")\n\t\t\tresponse.Error(c, response.ErrInternal, err)\n\t\t\treturn\n\t\t}\n\t\tc.DataFromReader(http.StatusOK, info.Size(), \"application/octet-stream\", f,\n\t\t\tmap[string]string{\n\t\t\t\t\"Content-Disposition\": mime.FormatMediaType(\"attachment\", map[string]string{\n\t\t\t\t\t\"filename\": e.rec.Name,","sourceCodeStart":1864,"sourceCodeEnd":1900,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/resources.go#L1864-L1900","documentation":"The resource's metadata row exists, but the content blob file backing it (content-addressed by hash under the server dataDir) is missing on disk. The service returns 404 ErrResourcesNotFound rather than 500 because the object is unrecoverable through the API.","triggerScenarios":"GET /resources/download where os.Open(BlobPath(dataDir, hash)) fails with ENOENT: the blob file was removed, the dataDir volume was recreated/mis-mounted, or deduplicated storage pruned a hash still referenced by a row.","commonSituations":"Docker volume for dataDir replaced or not mounted after redeploy; manual cleanup/gc scripts deleting blob files; backup restored only the DB without blob storage.","solutions":["Restore the blob store / dataDir volume from backup","Re-upload the resource to recreate its blob (then delete the dangling row if needed)","Verify the DATA_DIR env and container volume mounts point to the persistent store","Run a consistency check reconciling user_resources hashes against files on disk"],"exampleFix":"// before\ndocker compose up -d # dataDir volume not declared → blobs lost on redeploy\n// after\nvolumes:\n  - pentagi-data:/data  # persist blob storage\nservices:\n  backend:\n    volumes: [\"pentagi-data:/data\"]","handlingStrategy":"fallback","validationCode":"// server-side consistency check you can run as an operator\nrows := db.Query(\"SELECT path, hash FROM user_resources WHERE hash <> ''\")\nfor r := range rows {\n    if _, err := os.Stat(filepath.Join(dataDir, blobName(r.hash))); os.IsNotExist(err) {\n        log.Errorf(\"dangling resource %s: blob %s missing\", r.path, r.hash)\n    }\n}","typeGuard":null,"tryCatchPattern":"resp, _ := http.Get(downloadURL)\nif isErrResourcesNotFound(resp) && strings.Contains(body, \"not found on disk\") {\n    // blob unrecoverable via API: restore from backup or re-upload\n    return reuploadAndRetry(path)\n}","preventionTips":["Mount dataDir on a persistent Docker volume; never recreate it on deploy","Backup DB and blob store together, atomically","Exclude the blob directory from aggressive cleanup/GC scripts","Alert on hash-vs-disk mismatches (dangling rows)"],"tags":["filesystem","storage","download","blob"],"backgroundTag":"blob-missing-on-disk","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}