instructure/canvas-lms · error · InvalidDataError

An outcome group can only have one parent

Error message

An outcome group can only have one parent

What it means

find_parents resolves the possible parents for the group (from the import's parent/parent_guid fields or the prior group's current parent). A LearningOutcomeGroup has a single learning_outcome_group association, so if resolution yields more than one candidate the import cannot choose and raises this error.

Solutions

  1. Ensure only one parent reference resolves per group — supply either parent id or parent_guid, not conflicting both.
  2. Deduplicate groups that share a vendor_guid in overlapping contexts so guid resolution is unambiguous.
  3. Remove the old parent association before re-parenting in a single import row.
  4. Inspect find_parents to confirm which field it reads and blank the other one.

Example fix

// before
{ object_type: 'group', parent_id: 10, parent_guid: 'other-parent', ... }
// after
{ object_type: 'group', parent_guid: 'other-parent', ... }
Defensive patterns

Strategy: validation

Validate before calling

if group[:parent_id].present? && group[:parent_guid].present?
  p1 = LearningOutcomeGroup.find_by(id: group[:parent_id])
  p2 = LearningOutcomeGroup.find_by(vendor_guid: group[:parent_guid])
  raise ArgumentError, "conflicting parents" unless p1 == p2
end

Try / catch

begin
  importer.import_group(group)
rescue Outcomes::Import::InvalidDataError => e
  Rails.logger.warn("ambiguous parent for #{group[:vendor_guid]}: #{e.message}")
end

Prevention

When it happens

Trigger: Providing both a parent (canvas id) and parent_guid (vendor guid) on the same group row that resolve to different groups; the group's existing parent plus a new conflicting parent reference in the same import where both match distinct groups.

Common situations: CSVs with both legacy parent id columns and newer parent_guid columns filled in inconsistently; hand-edited rows where a stale parent_guid points to a renamed/duplicate group with the same guid in two contexts.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at lib/outcomes/import.rb:116

          raise InvalidDataError, I18n.t(
            "Target course %{course_id} is not a child of current account (%{name})",
            course_id: group[:course_id],
            name: context.name
          )
        end
      end

      model = find_prior_group(group, group_context)
      unless model.context == group_context
        raise InvalidDataError, I18n.t(
          "Group with ID %{guid} already exists in another unrelated course or account (%{name})",
          guid: group[:vendor_guid],
          name: model.context.name
        )
      end

      parents = find_parents(group, group_context, model:)
      raise InvalidDataError, I18n.t("An outcome group can only have one parent") if parents.length > 1

      parent = parents.first

      if model.outcome_import_id == outcome_import_id
        raise InvalidDataError, I18n.t(
          'Group "%{guid}" has already appeared in this import',
          guid: group[:vendor_guid]
        )
      end
      model.vendor_guid = group[:vendor_guid]
      model.title = group[:title]
      model.description = group[:description] || ""
      model.workflow_state = group[:workflow_state].presence || "active"
      model.learning_outcome_group = parent
      model.outcome_import_id = outcome_import_id
      model.save!

      if model.workflow_state == "deleted"

View on GitHub (pinned to 1c9f0bb801)