{"record":{"id":"4eafe05f9e87e60d","repo":"instructure/canvas-lms","slug":"must-be-a-group-assignment-in-order-to-create-group","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_creator_service.rb","lineNumber":42,"sourceCode":"  def initialize(checkpoint:, override:)\n    super()\n    @checkpoint = checkpoint\n    @override = override\n  end\n\n  def call\n    if differentiation_tag_override?(@override, @checkpoint)\n      handle_differentiation_tag_override\n    else\n      handle_group_override\n    end\n  end\n\n  private\n\n  def handle_group_override\n    if @checkpoint.effective_group_category_id.nil?\n      raise Checkpoints::GroupAssignmentRequiredError, \"must be a group assignment in order to create group overrides\"\n    end\n\n    group = get_group_from_override(@override, @checkpoint)\n    parent_override = parent_override(group)\n    create_override(assignment: @checkpoint, group:, parent_override:)\n  end\n\n  def handle_differentiation_tag_override\n    tag = get_differentiation_tag_from_override(@override, @checkpoint)\n    parent_override = parent_override(tag)\n    create_override(assignment: @checkpoint, group: tag, parent_override:)\n  end\n\n  def create_override(assignment:, group:, shell_override: false, parent_override: nil)\n    override = assignment.assignment_overrides.build(set: group, dont_touch_assignment: true, parent_override:)\n    apply_overridden_dates(override, @override, shell_override:)\n    override.save!\n    override","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/checkpoints/group_override_creator_service.rb#L24-L60","documentation":"Checkpoints::GroupAssignmentRequiredError raised in GroupOverrideCreatorService#handle_group_override when the checkpoint's effective_group_category_id is nil. Group overrides only make sense on group assignments; the service refuses to create one for an individual (non-group) checkpoint. effective_group_category_id resolves the checkpoint's inherited or own group category.","triggerScenarios":"Calling Checkpoints::GroupOverrideCreatorService for a checkpoint that is not a group assignment (no group category set on the checkpoint or its parent assignment).","commonSituations":"Frontend sends group overrides for a checkpoint whose parent was switched back to individual submission; a group category was deleted leaving the checkpoint individual; ordering bug where overrides are created before group_category_id is assigned.","solutions":["Set a group_category_id on the checkpoint/parent assignment before creating group overrides","Only submit group overrides when the assignment type is group_grade_by_group / has a group category","Verify checkpoint.effective_group_category_id is present in the client before sending the request","Rescue Checkpoints::GroupAssignmentRequiredError and return 422 explaining the assignment must be a group assignment"],"exampleFix":"// before\nservice = Checkpoints::GroupOverrideCreatorService.new(individual_checkpoint, { set_id: group.id })\nservice.call # raises\n// after\ncheckpoint.parent_assignment.update!(group_category_id: gc.id)\nservice = Checkpoints::GroupOverrideCreatorService.new(checkpoint, { set_id: group.id })","handlingStrategy":"validation","validationCode":"raise Checkpoints::GroupAssignmentRequiredError if checkpoint.effective_group_category_id.nil?\nCheckpoints::GroupOverrideCreatorService.new(checkpoint, override).call","typeGuard":"def group_checkpoint?(checkpoint)\n  checkpoint.effective_group_category_id.present?\nend","tryCatchPattern":"begin\n  Checkpoints::GroupOverrideCreatorService.new(checkpoint, override).call\nrescue Checkpoints::GroupAssignmentRequiredError\n  render json: { error: 'checkpoint must be a group assignment' }, status: :unprocessable_entity\nend","preventionTips":["Check effective_group_category_id before offering group override UI","Create overrides only after the group category is assigned","Clean up group overrides when an assignment reverts to individual"],"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"}