{"record":{"id":"1a05ddc704c8160c","repo":"vxcontrol/pentagi","slug":"failed-to-delete-file-blocking-directory-q-w","errorCode":null,"errorMessage":"failed to delete file blocking directory %q: %w","messagePattern":"failed to delete file blocking directory %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/server/services/resources.go","lineNumber":2102,"sourceCode":"\tparts := strings.Split(dirPath, \"/\")\n\tcreated := make([]models.UserResource, 0, len(parts))\n\tdeleted := make([]models.UserResource, 0, len(parts))\n\torphanHashes := make([]string, 0, len(parts))\n\tcurrent := \"\"\n\tfor _, part := range parts {\n\t\tcurrent = resources.FilePath(current, part)\n\n\t\texisting, exists, err := findResourceByPath(tx, uid, current)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\t\tif exists {\n\t\t\tif !existing.IsDir {\n\t\t\t\tif !force {\n\t\t\t\t\treturn nil, nil, nil, fmt.Errorf(\"%w: resource %q already exists and is not a directory\", errResourceConflict, current)\n\t\t\t\t}\n\t\t\t\tif err := tx.Delete(&existing).Error; err != nil {\n\t\t\t\t\treturn nil, nil, nil, fmt.Errorf(\"failed to delete file blocking directory %q: %w\", current, err)\n\t\t\t\t}\n\t\t\t\tdeleted = append(deleted, existing)\n\t\t\t\tif existing.Hash != \"\" {\n\t\t\t\t\torphanHashes = append(orphanHashes, existing.Hash)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\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}","sourceCodeStart":2084,"sourceCodeEnd":2120,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/resources.go#L2084-L2120","documentation":"ensureResourceDirs walks each path segment of a target resource directory and, when a segment exists in the DB as a FILE (not a dir) and the caller passed force=true, it deletes the blocking row first. This error wraps the underlying GORM/PostgreSQL failure of that forced delete (tx.Delete), e.g. FK constraint, connection loss, or serialization failure inside the transaction.","triggerScenarios":"Calling promoteToResources, UploadResources, moveMultipleSources, moveFileResource, moveDirResource, or copyMultipleSources with a dirPath whose intermediate segment collides with an existing file resource and force=true, and the DELETE statement fails (DB error: FK violation on referenced rows, dead connection, tx already aborted).","commonSituations":"User uploads a folder whose name matches an existing uploaded file while enabling 'overwrite/force'; the row being deleted is referenced by other tables (task logs, search logs) so Postgres rejects the delete; transient DB outage mid-transaction.","solutions":["Inspect the wrapped %w cause: if it is a foreign-key violation, remove or reassign rows referencing the resource (e.g. logs/attachments) before forcing the delete.","Retry the operation — transient DB/connection errors resolve on retry within a fresh transaction.","Verify DB connectivity and migration state (goose migrations applied) if the error is a missing table/column.","If the force-delete semantics are unwanted, retry the call with force=false to get the clean errResourceConflict instead."],"exampleFix":"// before\ncreated, deleted, orphans, err := svc.UploadResources(ctx, uid, dirPath, files, true) // force=true collides with file 'reports'\n// after\nexisting, _ := svc.FindResourceByPath(uid, \"reports\")\nif existing != nil && !existing.IsDir {\n    // clean blocking file references first, then upload\n    svc.DeleteResourceReferences(uid, \"reports\")\n}\ncreated, deleted, orphans, err := svc.UploadResources(ctx, uid, dirPath, files, true)","handlingStrategy":"try-catch","validationCode":"res, ok, _ := svc.FindResourceByPath(uid, dirPath)\nif ok && !res.IsDir && !forceAllowed {\n    return fmt.Errorf(\"path %q is an existing file; refusing dir upload\", dirPath)\n}","typeGuard":"func isFKViolation(err error) bool {\n    var pgErr *pgconn.PgError\n    return errors.As(err, &pgErr) && pgErr.Code == \"23503\"\n}","tryCatchPattern":"created, deleted, orphans, err := ensureResourceDirs(tx, uid, dirPath, force)\nif err != nil && strings.Contains(err.Error(), \"failed to delete file blocking directory\") {\n    // inspect wrapped cause with errors.Unwrap / errors.As(*pgconn.PgError)\n    // decide: retry, clean referencing rows, or surface conflict to caller\n}","preventionTips":["Check for path collisions with findResourceByPath before force-uploading a directory tree.","Clean up rows referencing the resource (logs, attachments) before forcing deletion.","Retry transient DB errors with backoff inside a fresh transaction.","Keep goose migrations current so FK definitions match expectations."],"tags":["database","gorm","resources","transaction"],"backgroundTag":"foreign-key-constraint-violation","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}