{"record":{"id":"13c8a0f004b0cf8a","repo":"instructure/canvas-lms","slug":"invalid-resource-group-key-resource-group-key","errorCode":null,"errorMessage":"Invalid resource group key: #{resource_group_key}","messagePattern":"Invalid resource group key: #(.+?)","errorType":"exception","errorClass":"UnsupportedResourceTypeError","httpStatus":400,"severity":"error","filePath":"app/services/youtube_migration_service.rb","lineNumber":183,"sourceCode":"    case resource_type\n    when /QuizzesNext::Quiz/\n      \"QuizzesNext::Quiz\"\n    when /QuizzesNext::Bank/\n      \"QuizzesNext::Bank\"\n    else\n      resource_type\n    end\n  end\n\n  def validate_supported_resource!(resource_type)\n    supported = SUPPORTED_RESOURCES.include?(resource_type) || NEW_QUIZZES_RESOURCES.include?(resource_type)\n    raise UnsupportedResourceTypeError, \"Unsupported resource type: #{resource_type}\" unless supported\n  end\n\n  def validate_resource_group_key!(resource_group_key)\n    parts = resource_group_key.to_s.split(\"|\")\n    if parts.size != 2 || parts.any?(&:blank?)\n      raise UnsupportedResourceTypeError, \"Invalid resource group key: #{resource_group_key}\"\n    end\n  end\n\n  def validate_resource_exists!(resource_type, resource_id)\n    # We don't store New Quizzes Data in Canvas, so we can't validate their existence\n    return true if NEW_QUIZZES_RESOURCES.include?(resource_type)\n\n    case resource_type\n    when \"WikiPage\"\n      course.wiki_pages.find(resource_id)\n    when \"Assignment\"\n      course.assignments.find(resource_id)\n    when \"DiscussionTopic\", \"Announcement\"\n      course.discussion_topics.find(resource_id)\n    when \"DiscussionEntry\"\n      DiscussionEntry.find(resource_id)\n    when \"CalendarEvent\"\n      course.calendar_events.find(resource_id)","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/youtube_migration_service.rb#L165-L201","documentation":"validate_resource_group_key! splits resource_group_key on '|' and requires exactly two non-blank parts. Keys like 'WikiPage', 'WikiPage|', '|123', or with extra pipes raise UnsupportedResourceTypeError because the key cannot identify a resource type + id pair.","triggerScenarios":"convert_embed with a malformed resource group key: missing id, empty type segment, an id containing a literal '|', or a key built by string concatenation with nil.","commonSituations":"Building the key manually as \"#{type}|#{id}\" where id is nil; splitting/rejoining keys incorrectly; keys copied from logs with truncation; ids that themselves contain '|' (rare, but breaks the 2-part rule).","solutions":["Build the key as \"#{resource_type}|#{resource_id}\" with both values present and nil-checked","Verify format before calling: key.to_s.split('|').size == 2 and no blank parts","If an id can contain '|', use a different join scheme and update the validator accordingly","Log/inspect the offending key — the message echoes exactly what was passed"],"exampleFix":"// before\nkey = \"WikiPage|#{page_id}\" # page_id is nil => 'WikiPage|'\n// after\nkey = [resource_type, resource_id].then { |t, i| raise 'missing id' if t.blank? || i.blank?; \"#{t}|#{i}\" }","handlingStrategy":"validation","validationCode":"def valid_resource_group_key?(key)\n  parts = key.to_s.split('|')\n  parts.size == 2 && parts.none?(&:blank?)\nend","typeGuard":"def parse_resource_group_key(key)\n  parts = key.to_s.split('|')\n  parts.size == 2 && parts.none?(&:blank?) ? parts : nil\nend","tryCatchPattern":"begin\n  service.convert_embed(scan_id, embed)\nrescue YoutubeMigrationService::UnsupportedResourceTypeError => e\n  raise ArgumentError, \"malformed key: #{e.message}\"\nend","preventionTips":["Always build keys as \"#{type}|#{id}\" with nil-checked values","Never let ids containing '|' reach this API","Unit-test key construction with nil/empty ids"],"tags":["ruby","validation","argument-format","migration"],"backgroundTag":"invalid-argument-format","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"}