{"record":{"id":"1302e97b46aa4dba","repo":"instructure/canvas-lms","slug":"unsupported-migration-type","errorCode":null,"errorMessage":"unsupported migration type","messagePattern":"unsupported migration type","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/canvas/migration/helpers/selective_content_formatter.rb","lineNumber":54,"sourceCode":"      [\"calendar_events\", -> { I18n.t(\"lib.canvas.migration.calendar_events\", \"Calendar Events\") }],\n      [\"rubrics\", -> { I18n.t(\"lib.canvas.migration.rubrics\", \"Rubrics\") }],\n      [\"groups\", -> { I18n.t(\"lib.canvas.migration.groups\", \"Student Groups\") }],\n      [\"learning_outcomes\", -> { I18n.t(\"lib.canvas.migration.learning_outcomes\", \"Learning Outcomes\") }],\n      [\"attachments\", -> { I18n.t(\"lib.canvas.migration.attachments\", \"Files\") }],\n    ].freeze\n\n    def initialize(migration = nil, base_url = nil, global_identifiers:)\n      @migration = migration\n      @base_url = base_url\n      @global_identifiers = global_identifiers\n    end\n\n    def valid_type?(type = nil)\n      type.nil? || SELECTIVE_CONTENT_TYPES.any? { |t| t[0] == type } || type.start_with?(\"submodules_\") || type.start_with?(\"learning_outcome_groups_\")\n    end\n\n    def get_content_list(type = nil, source = nil)\n      raise \"unsupported migration type\" unless valid_type?(type)\n\n      if !@migration || @migration.migration_type == \"course_copy_importer\"\n        get_content_from_course(type, source)\n      elsif @migration.overview_attachment\n        get_content_from_overview(type)\n      else\n        raise \"course hasn't been converted\"\n      end\n    end\n\n    private\n\n    def property_prefix\n      @migration ? \"copy\" : \"select\"\n    end\n\n    # pulls the available items from the overview attachment on the content migration\n    def get_content_from_overview(type = nil)","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/canvas/migration/helpers/selective_content_formatter.rb#L36-L72","documentation":"selective_content_formatter#get_content_list serves the list of migratable items per content type (assignments, quizzes, modules, etc.). It raises 'unsupported migration type' when the requested type is neither one of SELECTIVE_CONTENT_TYPES, nor a 'submodules_'/'learning_outcome_groups_' prefixed type, nor nil.","triggerScenarios":"Calling get_content_list('unknown_type') (optionally with source) on a SelectiveContentFormatter where valid_type? returns false — e.g. an arbitrary type string from an API client or a typo'd content type.","commonSituations":"Frontend/API sending a content type the backend version doesn't know (version skew); new content types added in one layer but not to SELECTIVE_CONTENT_TYPES; hand-crafted API requests.","solutions":["Check SELECTIVE_CONTENT_TYPES in selective_content_formatter.rb for the accepted type strings","Fix the caller to pass one of the supported types (or omit type for all)","Add the new type to SELECTIVE_CONTENT_TYPES if it is a legitimately new content category","Guard API clients against version skew by validating types against the course migration's available types"],"exampleFix":"// before\nformatter.get_content_list(\"wiki_pages_old\")\n// after\nformatter.get_content_list(\"wiki_pages\")","handlingStrategy":"validation","validationCode":"SELECTIVE_CONTENT_TYPES = %w[assignments quizzes modules files pages discussions announcements].freeze\nraise 'bad type' unless type.nil? || SELECTIVE_CONTENT_TYPES.include?(type)","typeGuard":"def supported_type?(t)\n  t.nil? || SELECTIVE_CONTENT_TYPES.include?(t) || t.start_with?('submodules_', 'learning_outcome_groups_')\nend","tryCatchPattern":"begin\n  formatter.get_content_list(type)\nrescue RuntimeError => e\n  raise unless e.message == 'unsupported migration type'\n  render json: {error: 'unsupported type'}, status: :bad_request\nend","preventionTips":["Derive API-allowed types from SELECTIVE_CONTENT_TYPES, not hardcoded lists","Keep frontend type names in sync with backend via shared constant/contract tests","Reject unknown types at the controller layer with a clear message"],"tags":["ruby","migration","api","content-selection"],"backgroundTag":"invalid-enum-value","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"}