{"record":{"id":"5b4659e33c2bf12e","repo":"instructure/canvas-lms","slug":"cannot-move-type-display-name-item-type-item-id-because-it","errorCode":null,"errorMessage":"Cannot move #{type_display_name(item_type)} #{item_id} because it has #{item_type}_memberships.","messagePattern":"Cannot move #(.+?) #(.+?) because it has #(.+?)_memberships\\.","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"lib/sis/group_importer.rb","lineNumber":147,"sourceCode":"          msg += item.errors.full_messages.join(\",\") + \")\"\n          raise ImportError, msg\n        end\n      end\n\n      def can_change_context?(item, new_context, item_type, item_id)\n        if item_type == \"differentiation_tag\"\n          raise ImportError, \"Differentiation Tags are not enabled for Account #{context.account.id}.\" unless new_context.account.allow_assign_to_differentiation_tags?\n        end\n\n        return true unless item&.group_memberships&.exists?\n\n        same_context = new_context.id == item.context_id &&\n                       new_context.class.base_class.name == item.context_type\n\n        # For groups, we only block moves between courses, but allow other context changes\n        if !same_context &&\n           ((item.context.is_a?(Course) || new_context.is_a?(Course)) || item_type == \"differentiation_tag\")\n          raise ImportError, \"Cannot move #{type_display_name(item_type)} #{item_id} because it has #{item_type}_memberships.\"\n        end\n\n        true\n      end\n\n      public\n\n      def add_group(group_id, group_category_id, account_id, course_id, name, status)\n        return if invalid_import_item?(group_id, name, status, \"group\")\n\n        if course_id && account_id\n          raise ImportError, \"Only one context is allowed and both course_id and account_id where provided for group #{group_id}.\"\n        end\n\n        context = find_context(account_id, :account_id, \"group\", group_id) if account_id\n        context ||= find_context(course_id, :course_id, \"group\", group_id) if course_id\n\n        group_category = nil","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/sis/group_importer.rb#L129-L165","documentation":"Raised by can_change_context? when an existing group or differentiation tag with memberships would be moved to a different context in a disallowed way: any move of a populated differentiation tag, and any populated-group move involving a Course context (source or destination). This protects membership integrity during SIS imports.","triggerScenarios":"add_group/add_tag updating an existing item (matched by sis id) whose group_memberships exist, where new_context differs from item.context and (either context is a Course, or item_type == 'differentiation_tag').","commonSituations":"Re-importing a groups.csv that reassigns a populated study group to another course; reorganizing account hierarchy with member-bearing groups; moving tags between courses mid-term.","solutions":["Remove or reassign the item's memberships before moving it.","Create a new group in the target context instead of moving the populated one.","For groups, allow only non-Course context changes (account reorganization).","Exclude the offending rows from the batch."],"exampleFix":"# before: moving populated group to another course\ngroup.context = new_course\ngroup.save\n# after\ngroup.group_memberships.destroy_all\ngroup.context = new_course\ngroup.save","handlingStrategy":"try-catch","validationCode":"if item.group_memberships.exists? &&\n   (new_context.is_a?(Course) || item.context.is_a?(Course) || item_type == 'differentiation_tag') &&\n   (new_context.id != item.context_id || new_context.class.base_class.name != item.context_type)\n  raise ArgumentError, 'cannot move item with memberships'\nend","typeGuard":null,"tryCatchPattern":"begin\n  importer.add_group(gid, cat_id, acct_id, course_id, name, status)\nrescue ImportError => e\n  if e.message.start_with?('Cannot move')\n    logger.warn(\"Skipped move of populated item #{gid}\")\n  else\n    raise\n  end\nend","preventionTips":["Avoid reassigning context for populated groups/tags in re-imports","Create new items in target contexts instead of moving populated ones","Diff incoming CSV context columns against current item context","Clear memberships deliberately before any move"],"tags":["sis-import","invalid-state","memberships"],"backgroundTag":"invalid-state-transition","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}