instructure/canvas-lms · error · Checkpoints::GroupAssignmentRequiredError
must be a group assignment in order to create group…
Error message
must be a group assignment in order to create group overrides
What it means
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.
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
Example fix
// before
service = Checkpoints::GroupOverrideCreatorService.new(individual_checkpoint, { set_id: group.id })
service.call # raises
// after
checkpoint.parent_assignment.update!(group_category_id: gc.id)
service = Checkpoints::GroupOverrideCreatorService.new(checkpoint, { set_id: group.id }) Defensive patterns
Strategy: validation
Validate before calling
raise Checkpoints::GroupAssignmentRequiredError if checkpoint.effective_group_category_id.nil? Checkpoints::GroupOverrideCreatorService.new(checkpoint, override).call
Type guard
def group_checkpoint?(checkpoint) checkpoint.effective_group_category_id.present? end
Try / catch
begin
Checkpoints::GroupOverrideCreatorService.new(checkpoint, override).call
rescue Checkpoints::GroupAssignmentRequiredError
render json: { error: 'checkpoint must be a group assignment' }, status: :unprocessable_entity
end Prevention
- 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
When it happens
Trigger: Calling Checkpoints::GroupOverrideCreatorService for a checkpoint that is not a group assignment (no group category set on the checkpoint or its parent assignment).
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- must be a group assignment in order to create group…
- Can't update submission scores unless it's completed
- set_id is required, but was not provided
- set_id is required, but was not provided
- set_id is required, but was not provided
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/4eafe05f9e87e60d.
Report an issue: GitHub.
Appendix: source
Thrown at app/services/checkpoints/group_override_creator_service.rb:42
def initialize(checkpoint:, override:)
super()
@checkpoint = checkpoint
@override = override
end
def call
if differentiation_tag_override?(@override, @checkpoint)
handle_differentiation_tag_override
else
handle_group_override
end
end
private
def handle_group_override
if @checkpoint.effective_group_category_id.nil?
raise Checkpoints::GroupAssignmentRequiredError, "must be a group assignment in order to create group overrides"
end
group = get_group_from_override(@override, @checkpoint)
parent_override = parent_override(group)
create_override(assignment: @checkpoint, group:, parent_override:)
end
def handle_differentiation_tag_override
tag = get_differentiation_tag_from_override(@override, @checkpoint)
parent_override = parent_override(tag)
create_override(assignment: @checkpoint, group: tag, parent_override:)
end
def create_override(assignment:, group:, shell_override: false, parent_override: nil)
override = assignment.assignment_overrides.build(set: group, dont_touch_assignment: true, parent_override:)
apply_overridden_dates(override, @override, shell_override:)
override.save!
overrideView on GitHub (pinned to 1c9f0bb801)