{"record":{"id":"758d758ddeb2cdab","repo":"instructure/canvas-lms","slug":"set-id-is-required-but-was-not-provided","errorCode":null,"errorMessage":"set_id is required, but was not provided","messagePattern":"set_id is required, but was not provided","errorType":"exception","errorClass":"Checkpoints::SetIdRequiredError","httpStatus":null,"severity":"error","filePath":"app/services/checkpoints/group_override_common.rb","lineNumber":37,"sourceCode":"\nmodule Checkpoints::GroupOverrideCommon\n  # Differentiaiton tags are allowed to be used as group overrides\n  # This method determines if the override is associated with a\n  # differentiation tag in the checkpoint's course\n  def differentiation_tag_override?(override, checkpoint)\n    return false unless differentiation_tags_enabled_for_context?(checkpoint)\n\n    group = get_differentiation_tag_from_override(override, checkpoint)\n\n    if group.nil?\n      return false\n    end\n\n    group.non_collaborative\n  end\n\n  def get_group_from_override(override, checkpoint)\n    group_id = override.fetch(:set_id) { raise Checkpoints::SetIdRequiredError, \"set_id is required, but was not provided\" }\n    checkpoint.course.active_groups.where(group_category_id: checkpoint.effective_group_category_id).find(group_id)\n  end\n\n  def get_differentiation_tag_from_override(override, checkpoint)\n    tag_id = override.fetch(:set_id) { raise Checkpoints::SetIdRequiredError, \"set_id is required, but was not provided\" }\n    checkpoint.course.differentiation_tags.where(id: tag_id).first\n  end\n\n  private\n\n  def differentiation_tags_enabled_for_context?(checkpoint)\n    account = checkpoint.course.account\n    account.allow_assign_to_differentiation_tags?\n  end\nend\n","sourceCodeStart":19,"sourceCodeEnd":53,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/checkpoints/group_override_common.rb#L19-L53","documentation":"Checkpoints::SetIdRequiredError raised by Checkpoints::GroupOverrideCommon#get_group_from_override when the override hash passed in has no :set_id key. The method uses Hash#fetch with a raise-on-missing block, so any group override payload lacking set_id aborts before the group is looked up. It exists to fail fast with a clear message instead of a confusing nil find.","triggerScenarios":"Calling get_group_from_override with an override hash that omits the :set_id key, e.g. creating/updating a group-set checkpoint override from a form that did not submit set_id or called the service with {assignment_override: {...}} minus set_id.","commonSituations":"API clients posting checkpoint overrides without set_id; frontend diffing code that only sends changed attributes so set_id is dropped on unchanged overrides; refactors that renamed group_id to set_id but callers still send the old key.","solutions":["Add set_id (the group id) to the override hash before calling the service","Ensure the client always submits set_id for group overrides, not only on change","If the override already exists, read set_id from the persisted AssignmentOverride (override.set_id) as the updater service does","Rescue Checkpoints::SetIdRequiredError at the controller boundary and return 400 with the message"],"exampleFix":"// before\nservice = Checkpoints::GroupOverrideCreatorService.new(checkpoint, { due_at: t })\n// after\nservice = Checkpoints::GroupOverrideCreatorService.new(checkpoint, { set_id: group.id, due_at: t })","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'set_id is required' unless override.is_a?(Hash) && override[:set_id].present?\n# then call the service\nCheckpoints::GroupOverrideCreatorService.new(checkpoint, override).call","typeGuard":"def valid_group_override?(override)\n  override.is_a?(Hash) && override[:set_id].present?\nend","tryCatchPattern":"begin\n  Checkpoints::GroupOverrideCreatorService.new(checkpoint, override).call\nrescue Checkpoints::SetIdRequiredError => e\n  render json: { errors: ['set_id is required for group overrides'] }, status: :bad_request\nend","preventionTips":["Always send set_id with every override payload, even for partial updates","Validate override hashes with a schema (presence of set_id) before service calls","Merge persisted override.set_id into update payloads server-side"],"tags":["rails","checkpoints","group-overrides","missing-parameter"],"backgroundTag":"missing-required-argument","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"}