{"record":{"id":"44939d13fc50084f","repo":"vxcontrol/pentagi","slug":"failed-to-create-resource-directory-q-w","errorCode":null,"errorMessage":"failed to create resource directory %q: %w","messagePattern":"failed to create resource directory %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/server/services/resources.go","lineNumber":2131,"sourceCode":"\t\trec := models.UserResource{\n\t\t\tUserID: uid,\n\t\t\tHash:   \"\",\n\t\t\tName:   path.Base(current),\n\t\t\tPath:   current,\n\t\t\tSize:   0,\n\t\t\tIsDir:  true,\n\t\t}\n\t\tif err := tx.Create(&rec).Error; err != nil {\n\t\t\tif isUniqueViolation(err) {\n\t\t\t\trefetched, ok, refetchErr := findResourceByPath(tx, uid, current)\n\t\t\t\tif refetchErr != nil {\n\t\t\t\t\treturn nil, nil, nil, refetchErr\n\t\t\t\t}\n\t\t\t\tif ok && refetched.IsDir {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, nil, nil, fmt.Errorf(\"failed to create resource directory %q: %w\", current, err)\n\t\t}\n\t\tcreated = append(created, rec)\n\t}\n\n\treturn created, deleted, orphanHashes, nil\n}\n\n// deleteOrphanBlob removes the .blob for hash if no DB row references it.\nfunc (s *ResourceService) deleteOrphanBlob(_ context.Context, hash string) {\n\tif hash == \"\" {\n\t\treturn\n\t}\n\tvar count int64\n\tif err := s.db.Model(&models.UserResource{}).\n\t\tWhere(\"hash = ?\", hash).\n\t\tCount(&count).Error; err != nil || count > 0 {\n\t\treturn\n\t}","sourceCodeStart":2113,"sourceCodeEnd":2149,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/resources.go#L2113-L2149","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n_, err := svc.UploadResources(ctx, uid, \"a/b/c\", files, false) // concurrent upload raced on 'a/b'\n// after\nerr := retry.OnError(3, isUniqueViolation, func() error {\n    _, err := svc.UploadResources(ctx, uid, \"a/b/c\", files, false)\n    return err\n})","handlingStrategy":"retry","validationCode":"for _, seg := range strings.Split(dirPath, \"/\") {\n    if existing, ok, _ := svc.FindResourceByPath(uid, segPath); ok && !existing.IsDir {\n        return fmt.Errorf(\"segment %q is a file\", seg)\n    }\n}","typeGuard":"func isUniqueViolation(err error) bool {\n    var pgErr *pgconn.PgError\n    return errors.As(err, &pgErr) && pgErr.Code == \"23505\"\n}","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"failed to create resource directory\") {\n    if isUniqueViolation(errors.Unwrap(err)) {\n        // re-list resources and retry once; the concurrent writer likely finished\n    }\n}","preventionTips":["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."],"tags":["database","gorm","resources","unique-constraint"],"backgroundTag":"unique-constraint-violation","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}