{"record":{"id":"6b3c7d810e060911","repo":"instructure/canvas-lms","slug":"resource-not-found-for-type-resource-type-id-resource-id","errorCode":null,"errorMessage":"Resource not found for type: #{resource_type}, id: #{resource_id}","messagePattern":"Resource not found for type: #(.+?), id: #(.+?)","errorType":"exception","errorClass":"ResourceNotFoundError","httpStatus":404,"severity":"error","filePath":"app/services/youtube_migration_service.rb","lineNumber":215,"sourceCode":"      course.discussion_topics.find(resource_id)\n    when \"DiscussionEntry\"\n      DiscussionEntry.find(resource_id)\n    when \"CalendarEvent\"\n      course.calendar_events.find(resource_id)\n    when \"Quizzes::Quiz\"\n      course.quizzes.find(resource_id)\n    when \"Quizzes::QuizQuestion\"\n      Quizzes::QuizQuestion.find(resource_id)\n    when \"AssessmentQuestion\"\n      AssessmentQuestion.find(resource_id)\n    when \"CourseSyllabus\", \"Course\"\n      # For syllabus, the resource_id is the course id\n      raise ResourceNotFoundError, \"Course not found\" unless course.id == resource_id\n    else\n      raise ResourceNotFoundError, \"Cannot validate existence for resource type: #{resource_type}\"\n    end\n  rescue ActiveRecord::RecordNotFound\n    raise ResourceNotFoundError, \"Resource not found for type: #{resource_type}, id: #{resource_id}\"\n  end\n\n  def convert_embed(scan_id, embed, user_uuid: nil)\n    validate_scan_exists!(scan_id)\n    validate_supported_resource!(embed[:resource_type])\n\n    resource_group_key = embed[:resource_group_key] || YoutubeMigrationService.generate_resource_key(embed[:resource_type], embed[:id])\n    validate_resource_group_key!(resource_group_key)\n    validate_embed_exists_in_scan!(scan_id, embed)\n    validate_resource_exists!(embed[:resource_type], embed[:id])\n\n    message = YoutubeMigrationService.generate_resource_key(embed[:resource_type], embed[:id])\n    # TODO: Something will listen on this creation\n    convert_progress = Progress.create!(tag: CONVERT_TAG, context: course, message:, results: { original_embed: embed })\n\n    job_priority = Account.site_admin.feature_enabled?(:youtube_migration_high_priority) ? Delayed::HIGH_PRIORITY : Delayed::LOW_PRIORITY\n    n_strand = \"youtube_embed_convert_#{course.global_id}_#{resource_group_key}\"\n    convert_progress.process_job(YoutubeMigrationService, :perform_conversion, { n_strand:, priority: job_priority }, course.id, scan_id, embed, user_uuid:)","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/services/youtube_migration_service.rb#L197-L233","documentation":"validate_resource_exists! looks up the actual record (e.g. WikiPage, Quizzes::Quiz, QuizQuestion, AssessmentQuestion, DiscussionTopic, Assignment, etc.) with .find. ActiveRecord::RecordNotFound is rescued and re-raised as ResourceNotFoundError naming the type and id, so conversion stops when the backing content no longer exists.","triggerScenarios":"convert_embed where resource_id references a deleted record, an id from another course/shard, or a type whose id semantics differ (e.g. passing a quiz question's assessment_question id).","commonSituations":"Content deleted between scan and convert (weeks can pass); restoring/importing courses that reassign ids; migrating data across environments; soft-deleted or unpublished records the scan once saw.","solutions":["Verify the record still exists for that id in the same shard/course before converting; re-run the scan to refresh ids if content was recreated","Skip this embed and continue converting the rest, logging the dangling reference","If content was deleted intentionally, remove the embed from the content rather than converting","Check that the resource_type matches the id's actual model (QuizQuestion id vs AssessmentQuestion id mix-ups)"],"exampleFix":"// before\nservice.convert_embed(scan_id, { resource_type: 'Quizzes::Quiz', id: deleted_quiz_id, ... })\n// after\nbegin\n  service.convert_embed(scan_id, embed)\nrescue YoutubeMigrationService::ResourceNotFoundError => e\n  Rails.logger.warn(\"skipping unconvertible embed: #{e.message}\")\nend","handlingStrategy":"try-catch","validationCode":"model = embed[:resource_type].safe_constantize\nraise 'record missing' unless model && embed[:resource_type] != 'CourseSyllabus' && model.where(id: embed[:id]).exists?","typeGuard":null,"tryCatchPattern":"begin\n  service.convert_embed(scan_id, embed)\nrescue YoutubeMigrationService::ResourceNotFoundError => e\n  Rails.logger.warn(\"dangling embed skipped: #{e.message}\")\n  next # continue with remaining embeds\nend","preventionTips":["Convert soon after scanning to minimize deletion windows","Handle skips gracefully in bulk conversion instead of aborting","Re-scan after content imports/deletes to refresh ids"],"tags":["ruby","activerecord","record-not-found","migration"],"backgroundTag":"record-not-found","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"}