vxcontrol/pentagi · error
failed to create resource directory %q: %w
Error message
failed to create resource directory %q: %w
What it means
After ensuring each path segment exists, ensureResourceDirs creates a directory row (IsDir=true). If tx.Create fails — and even after a unique-violation refetch the row is still missing or still a file — the create error is wrapped with this message. Typical causes are unique violations against a non-dir row (concurrent write), or generic DB failures.
Source
Thrown at backend/pkg/server/services/resources.go:2131
rec := models.UserResource{
UserID: uid,
Hash: "",
Name: path.Base(current),
Path: current,
Size: 0,
IsDir: true,
}
if err := tx.Create(&rec).Error; err != nil {
if isUniqueViolation(err) {
refetched, ok, refetchErr := findResourceByPath(tx, uid, current)
if refetchErr != nil {
return nil, nil, nil, refetchErr
}
if ok && refetched.IsDir {
continue
}
}
return nil, nil, nil, fmt.Errorf("failed to create resource directory %q: %w", current, err)
}
created = append(created, rec)
}
return created, deleted, orphanHashes, nil
}
// deleteOrphanBlob removes the .blob for hash if no DB row references it.
func (s *ResourceService) deleteOrphanBlob(_ context.Context, hash string) {
if hash == "" {
return
}
var count int64
if err := s.db.Model(&models.UserResource{}).
Where("hash = ?", hash).
Count(&count).Error; err != nil || count > 0 {
return
}View on GitHub (pinned to ea665308ba)
Solutions
- Check the wrapped cause: if unique violation, re-list resources — the dir (or a conflicting file) now exists; retry the call.
- Serialize directory creation per user (lock/mutex or SELECT ... FOR UPDATE) if concurrent uploads of the same path are common.
- Verify DB is writable (disk space, read-only replica) if errors persist.
- If a file blocks the path, the segment loop already skipped it only when force=false errors out earlier; use force=true to remove the blocking file first.
Example fix
// before
_, err := svc.UploadResources(ctx, uid, "a/b/c", files, false) // concurrent upload raced on 'a/b'
// after
err := retry.OnError(3, isUniqueViolation, func() error {
_, err := svc.UploadResources(ctx, uid, "a/b/c", files, false)
return err
}) Defensive patterns
Strategy: retry
Validate before calling
for _, seg := range strings.Split(dirPath, "/") {
if existing, ok, _ := svc.FindResourceByPath(uid, segPath); ok && !existing.IsDir {
return fmt.Errorf("segment %q is a file", seg)
}
} Type guard
func isUniqueViolation(err error) bool {
var pgErr *pgconn.PgError
return errors.As(err, &pgErr) && pgErr.Code == "23505"
} Try / catch
if err != nil && strings.Contains(err.Error(), "failed to create resource directory") {
if isUniqueViolation(errors.Unwrap(err)) {
// re-list resources and retry once; the concurrent writer likely finished
}
} Prevention
- Serialize directory-creating operations per user (lock or advisory lock) to avoid same-path races.
- Retry once on unique violation — the code already refetches, but the refetched row may briefly not be a dir.
- Monitor disk space / DB writability; creation failures can be environmental.
- Pre-create directory trees in a single call rather than concurrent partial uploads.
When it happens
Trigger: Any of promoteToResources/UploadResources/move*/copyMultipleSources calls with a dirPath segment that (a) does not exist and the INSERT fails (unique violation from a concurrent request creating the same path as a file, connection drop), or (b) hits a unique violation but the refetched row is not a directory.
Common situations: Two concurrent uploads creating the same directory tree race with each other; a file with the same path was created between the findResourceByPath check and the Create; database is read-only or out of disk.
Related errors
- failed to fetch resources: %w
- failed to move resource %q: %w
- failed to copy resource: %w
- failed to copy resource %q: %w
- failed to list resources: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/44939d13fc50084f.
Report an issue: GitHub.