{"record":{"id":"29073adb96faafc9","repo":"instructure/canvas-lms","slug":"outcome-guid-has-already-appeared-in-this-import","errorCode":null,"errorMessage":"Outcome \"%{guid}\" has already appeared in this import","messagePattern":"Outcome \"%(.+?)\" has already appeared in this import","errorType":"exception","errorClass":"InvalidDataError","httpStatus":null,"severity":"error","filePath":"lib/outcomes/import.rb","lineNumber":167,"sourceCode":"        )\n      end\n\n      model = find_prior_outcome(outcome)\n      model.context = context if model.new_record?\n\n      unless child_context?(context, of: model.context)\n        raise InvalidDataError, I18n.t(\n          \"Outcome with ID %{guid} already exists in another unrelated course or account (%{name})\",\n          guid: outcome[:vendor_guid],\n          name: model.context.name\n        )\n      end\n\n      allow_indirect = outcome.key?(:course_id)\n      parents = find_parents(outcome, context, allow_indirect:)\n\n      if model.outcome_import_id == outcome_import_id\n        raise InvalidDataError, I18n.t(\n          'Outcome \"%{guid}\" has already appeared in this import',\n          guid: outcome[:vendor_guid]\n        )\n      end\n\n      model.vendor_guid = outcome[:vendor_guid]\n      model.title = outcome[:title]\n      model.description = infer_nil_value(model, :description, outcome)\n      model.display_name = infer_nil_value(model, :display_name, outcome)\n      model.calculation_method = outcome[:calculation_method].presence || model.default_calculation_method\n      model.calculation_int = outcome[:calculation_int].presence || model.default_calculation_int\n      # let removing the outcome_links content tags delete the underlying outcome\n      model.workflow_state = \"active\" unless outcome[:workflow_state] == \"deleted\"\n\n      prior_rubric = model.rubric_criterion || {}\n      changed = ->(k) { outcome[k].present? && outcome[k] != prior_rubric[k] }\n      rubric_change = changed.call(:ratings) || changed.call(:mastery_points)\n      model.rubric_criterion = create_rubric(outcome[:ratings], outcome[:mastery_points]) if rubric_change","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/outcomes/import.rb#L149-L185","documentation":"Raised when import_outcome finds a prior outcome (by vendor_guid) whose outcome_import_id already equals the current import's id, meaning the same vendor_guid row has been processed twice within one outcome import. The library treats duplicate rows in a single import as data corruption, since a guid must uniquely identify an outcome per import run.","triggerScenarios":"An outcome import file (CSV/JSON) contains two rows with the same vendor_guid; import_outcome checks model.outcome_import_id == outcome_import_id at lib/outcomes/import.rb:167.","commonSituations":"Manually merged or concatenated outcome export files; CSV rows duplicated by a copy/paste mistake; a script that retries rows without deduplicating before upload.","solutions":["Deduplicate the import file so each vendor_guid appears only once.","If both rows are intentional updates, merge their fields into one row keyed by the single vendor_guid.","Fix the generating export/script that emitted the duplicate guid."],"exampleFix":"// before: rows [{vendor_guid:'A',...},{vendor_guid:'A',...}]\n// after: merge duplicates\n{\n  \"vendor_guid\": \"A\",\n  \"title\": \"merged title\",\n  \"ratings\": [/* merged ratings */]\n}","handlingStrategy":"validation","validationCode":"// Ruby: dedupe rows by vendor_guid before upload\nguids = rows.map { |r| r[:vendor_guid] }\nraise 'duplicate guids' if guids.uniq.size != guids.size\nrows.uniq_by { |r| r[:vendor_guid] }","typeGuard":null,"tryCatchPattern":"begin\n  importer.import_object(...)\nrescue Outcomes::Import::InvalidDataError => e\n  raise unless e.message.include?('already appeared in this import')\n  # drop later duplicate rows and re-queue the import\nend","preventionTips":["Deduplicate import files on vendor_guid before creating the outcome import.","Never merge exports from multiple sources without re-keying guids.","Make retry logic idempotent at the file level, not per row."],"tags":["outcomes","import","duplicate-key","data-validation"],"backgroundTag":"duplicate-key","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"}