{"record":{"id":"714fb98e4401744a","repo":"instructure/canvas-lms","slug":"must-be-a-group-assignment-in-order-to-create-group-override","errorCode":null,"errorMessage":"must be a group assignment in order to create group overrides","messagePattern":"must be a group assignment in order to create group overrides","errorType":"exception","errorClass":"Checkpoints::GroupAssignmentRequiredError","httpStatus":null,"severity":"error","filePath":"app/services/checkpoints/group_override_updater_service.rb","lineNumber":33,"sourceCode":"# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n\nclass Checkpoints::GroupOverrideUpdaterService < ApplicationService\n  include Checkpoints::DateOverrider\n  include Checkpoints::GroupOverrideCommon\n\n  def initialize(checkpoint:, override:)\n    super()\n    @checkpoint = checkpoint\n    @override = override\n  end\n\n  def call\n    is_differentiation_tag_override = differentiation_tag_override?(@override, @checkpoint)\n    if @checkpoint.effective_group_category_id.nil? && !is_differentiation_tag_override\n      raise Checkpoints::GroupAssignmentRequiredError, \"must be a group assignment in order to create group overrides\"\n    end\n\n    override = @checkpoint.assignment_overrides.find_by(id: @override[:id], set_type: AssignmentOverride::SET_TYPE_GROUP)\n    raise Checkpoints::OverrideNotFoundError unless override\n\n    group_id = @override.fetch(:set_id, nil) || override.set_id\n\n    raise Checkpoints::SetIdRequiredError, \"set_id is required, but was not provided\" if group_id.blank?\n\n    group = is_differentiation_tag_override ? get_differentiation_tag_from_override(@override, @checkpoint) : get_group_from_override(@override, @checkpoint)\n\n    current_group = override.set\n\n    update_override(override:, group:)\n\n    parent_override = @checkpoint.parent_assignment.active_assignment_overrides.find_by(set: current_group)\n    raise Checkpoints::OverrideNotFoundError unless parent_override\n","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/checkpoints/group_override_updater_service.rb#L15-L51","documentation":"Checkpoints::GroupAssignmentRequiredError raised in GroupOverrideUpdaterService#call when the checkpoint has no effective group category AND the override is not a differentiation-tag override. Updates to group overrides require the checkpoint to remain a group assignment; the exception names 'create' because it shares the message with the creator.","triggerScenarios":"Calling the updater with a group-set override payload for a checkpoint whose effective_group_category_id is nil and whose payload is not tagged as a differentiation-tag override (e.g. set_id points to a tag but differentiation tag path not detected).","commonSituations":"Assignment reverted from group to individual while old group overrides still exist and get updated; client updates an override whose set_type no longer matches the checkpoint's submission type.","solutions":["Restore a group category on the checkpoint/parent assignment before updating group overrides","Delete stale group overrides instead of updating them when the assignment became individual","Ensure differentiation-tag detection data (tags enabled for context, correct set_id) so the tag path is taken when intended","Rescue Checkpoints::GroupAssignmentRequiredError and return 422"],"exampleFix":"// before\nCheckpoints::GroupOverrideUpdaterService.new(individual_checkpoint, { id: id, set_id: gid }).call\n// after\nreturn unless checkpoint.effective_group_category_id.present?\nCheckpoints::GroupOverrideUpdaterService.new(checkpoint, { id: id, set_id: gid }).call","handlingStrategy":"validation","validationCode":"if checkpoint.effective_group_category_id.nil?\n  return render json: { error: 'not a group assignment' }, status: :unprocessable_entity\nend\nCheckpoints::GroupOverrideUpdaterService.new(checkpoint, override).call","typeGuard":"def updatable_group_override?(checkpoint, override)\n  checkpoint.effective_group_category_id.present? || override[:set_id].present?\nend","tryCatchPattern":"begin\n  Checkpoints::GroupOverrideUpdaterService.new(checkpoint, override).call\nrescue Checkpoints::GroupAssignmentRequiredError\n  head :unprocessable_entity\nend","preventionTips":["Delete stale group overrides when assignment submission type changes","Gate update endpoints on checkpoint group-assignment state","Keep parent assignment group category in sync with checkpoints"],"tags":["rails","checkpoints","group-assignments","invalid-state"],"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"}