{"record":{"id":"fb5ff522ccf4be2f","repo":"instructure/canvas-lms","slug":"could-not-update-parent-group","errorCode":null,"errorMessage":"Could not update parent group","messagePattern":"Could not update parent group","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/update_learning_outcome_group.rb","lineNumber":41,"sourceCode":"\n  argument :description, String, required: false\n  argument :id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"LearningOutcomeGroup\")\n  argument :parent_outcome_group_id, ID, required: false\n  argument :title, String, required: false\n  argument :vendor_guid, String, required: false\n\n  field :learning_outcome_group, Types::LearningOutcomeGroupType, null: true\n\n  def resolve(input:)\n    @outcome_group = get_group(input[:id])\n\n    check_user_permissions\n\n    @outcome_group.saving_user = current_user\n    if @outcome_group.update(attributes(input))\n      if input[:parent_outcome_group_id] && input[:parent_outcome_group_id] != @outcome_group.learning_outcome_group_id\n        new_parent_group = get_parent_group(input[:parent_outcome_group_id])\n        raise GraphQL::ExecutionError, I18n.t(\"Could not update parent group\") unless new_parent_group.adopt_outcome_group(@outcome_group)\n      end\n      { learning_outcome_group: @outcome_group }\n    else\n      errors_for(@outcome_group)\n    end\n  end\n\n  private\n\n  def get_group(id)\n    LearningOutcomeGroup.active.find_by(id:).tap do |group|\n      raise GraphQL::ExecutionError, I18n.t(\"Group not found\") unless group\n    end\n  end\n\n  def get_parent_group(id)\n    LearningOutcomeGroup.for_context(@outcome_group.context).active.find_by(id:).tap do |group|\n      raise GraphQL::ExecutionError, I18n.t(\"Parent group not found in this context\") unless group","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/update_learning_outcome_group.rb#L23-L59","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the new parent is not the group itself or one of its descendants (avoid cycles).","Run the move manually in console (new_parent.adopt_outcome_group(group)) to see the underlying failure.","Verify both groups' contexts are compatible (same account/course lineage).","Retry after fixing hierarchy data, or move the group to a valid parent id."],"exampleFix":"// before\nupdateLearningOutcomeGroup(input: {id: \"5\", parentOutcomeGroupId: \"7\"}) // 7 is a descendant of 5\n// after\nupdateLearningOutcomeGroup(input: {id: \"5\", parentOutcomeGroupId: \"2\"}) // unrelated root-level group","handlingStrategy":"validation","validationCode":"function isValidReparent(group, newParentId) {\n  if (newParentId == null) return true;\n  if (String(newParentId) === String(group.id)) return false;\n  return !isDescendant(group, newParentId); // walk ancestors of new parent\n}","typeGuard":null,"tryCatchPattern":"try {\n  await gql(UPDATE_LEARNING_OUTCOME_GROUP, { id, parentOutcomeGroupId: parentId });\n} catch (e) {\n  if (e.message === 'Could not update parent group') { /* revert UI tree, offer valid parents */ }\n  else throw e;\n}","preventionTips":["Never allow moving a group under itself or a descendant","Keep group hierarchies within the same context","Test deep/nested group trees before restructuring"],"tags":["graphql","outcome-groups","hierarchy","adopt-failed"],"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"}