vxcontrol/pentagi · error
blob for resource %q not found on disk
Error message
blob for resource %q not found on disk
What it means
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.
Source
Thrown at backend/pkg/server/services/resources.go:1882
response.Error(c, response.ErrResourcesNotFound, fmt.Errorf("resource %q not found", targetPath))
return
}
logger.FromContext(c).WithError(err).Error("error finding resource for download")
response.Error(c, response.ErrInternal, err)
return
}
entries = append(entries, resolvedEntry{rec: rec})
}
// Single regular file → serve as a direct attachment with explicit Content-Length.
if len(entries) == 1 && !entries[0].rec.IsDir {
e := entries[0]
blobPath := resources.BlobPath(s.dataDir, e.rec.Hash)
f, err := os.Open(blobPath)
if err != nil {
if os.IsNotExist(err) {
response.Error(c, response.ErrResourcesNotFound,
fmt.Errorf("blob for resource %q not found on disk", e.rec.Path))
return
}
logger.FromContext(c).WithError(err).Error("error opening resource blob for download")
response.Error(c, response.ErrInternal, err)
return
}
defer f.Close()
info, err := f.Stat()
if err != nil {
logger.FromContext(c).WithError(err).Error("error stating resource blob for download")
response.Error(c, response.ErrInternal, err)
return
}
c.DataFromReader(http.StatusOK, info.Size(), "application/octet-stream", f,
map[string]string{
"Content-Disposition": mime.FormatMediaType("attachment", map[string]string{
"filename": e.rec.Name,View on GitHub (pinned to ea665308ba)
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
Example fix
// before
docker compose up -d # dataDir volume not declared → blobs lost on redeploy
// after
volumes:
- pentagi-data:/data # persist blob storage
services:
backend:
volumes: ["pentagi-data:/data"] Defensive patterns
Strategy: fallback
Validate before calling
// server-side consistency check you can run as an operator
rows := db.Query("SELECT path, hash FROM user_resources WHERE hash <> ''")
for r := range rows {
if _, err := os.Stat(filepath.Join(dataDir, blobName(r.hash))); os.IsNotExist(err) {
log.Errorf("dangling resource %s: blob %s missing", r.path, r.hash)
}
} Try / catch
resp, _ := http.Get(downloadURL)
if isErrResourcesNotFound(resp) && strings.Contains(body, "not found on disk") {
// blob unrecoverable via API: restore from backup or re-upload
return reuploadAndRetry(path)
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to commit blob %s: %w
- failed to stat blob %s: %w
- failed to get flow screenshot: %w
- failed to get absolute path: %w
- failed to create tmp directory: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/4cecaa4adb43e377.
Report an issue: GitHub.