instructure/canvas-lms · error · GraphQL::ExecutionError

Could not update parent group

Error message

Could not update parent group

What it means

This error is raised in UpdateLearningOutcomeGroup#resolve when reparenting an outcome group fails: a new parent group was found, but adopt_outcome_group returned false, so the parent update could not be completed. It is a domain-level failure, not a validation error.

Solutions

  1. Check the new parent is not the group itself or one of its descendants (avoid cycles).
  2. Run the move manually in console (new_parent.adopt_outcome_group(group)) to see the underlying failure.
  3. Verify both groups' contexts are compatible (same account/course lineage).
  4. Retry after fixing hierarchy data, or move the group to a valid parent id.

Example fix

// before
updateLearningOutcomeGroup(input: {id: "5", parentOutcomeGroupId: "7"}) // 7 is a descendant of 5
// after
updateLearningOutcomeGroup(input: {id: "5", parentOutcomeGroupId: "2"}) // unrelated root-level group
Defensive patterns

Strategy: validation

Validate before calling

function isValidReparent(group, newParentId) {
  if (newParentId == null) return true;
  if (String(newParentId) === String(group.id)) return false;
  return !isDescendant(group, newParentId); // walk ancestors of new parent
}

Try / catch

try {
  await gql(UPDATE_LEARNING_OUTCOME_GROUP, { id, parentOutcomeGroupId: parentId });
} catch (e) {
  if (e.message === 'Could not update parent group') { /* revert UI tree, offer valid parents */ }
  else throw e;
}

Prevention

When it happens

Trigger: Passing parent_outcome_group_id to updateLearningOutcomeGroup and the adopt step fails — typically because adopt_outcome_group hits a validation/constraint (e.g. cycle, depth, persistence error) while moving the group under the new parent.

Common situations: Attempting to move a group under its own descendant (cycle); outcome groups with conflicting contexts; DB constraint failures during the bulk update of affected groups; corrupt or deeply nested group trees.

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


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/fb5ff522ccf4be2f. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/update_learning_outcome_group.rb:41

  argument :description, String, required: false
  argument :id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("LearningOutcomeGroup")
  argument :parent_outcome_group_id, ID, required: false
  argument :title, String, required: false
  argument :vendor_guid, String, required: false

  field :learning_outcome_group, Types::LearningOutcomeGroupType, null: true

  def resolve(input:)
    @outcome_group = get_group(input[:id])

    check_user_permissions

    @outcome_group.saving_user = current_user
    if @outcome_group.update(attributes(input))
      if input[:parent_outcome_group_id] && input[:parent_outcome_group_id] != @outcome_group.learning_outcome_group_id
        new_parent_group = get_parent_group(input[:parent_outcome_group_id])
        raise GraphQL::ExecutionError, I18n.t("Could not update parent group") unless new_parent_group.adopt_outcome_group(@outcome_group)
      end
      { learning_outcome_group: @outcome_group }
    else
      errors_for(@outcome_group)
    end
  end

  private

  def get_group(id)
    LearningOutcomeGroup.active.find_by(id:).tap do |group|
      raise GraphQL::ExecutionError, I18n.t("Group not found") unless group
    end
  end

  def get_parent_group(id)
    LearningOutcomeGroup.for_context(@outcome_group.context).active.find_by(id:).tap do |group|
      raise GraphQL::ExecutionError, I18n.t("Parent group not found in this context") unless group

View on GitHub (pinned to 1c9f0bb801)