{"record":{"id":"1581f842eee77acc","repo":"instructure/canvas-lms","slug":"file-required-for-content-migration","errorCode":null,"errorMessage":"File required for content migration.","messagePattern":"File required for content migration\\.","errorType":"exception","errorClass":"Canvas::Migration::Error","httpStatus":null,"severity":"error","filePath":"lib/cc/importer/cc_worker.rb","lineNumber":40,"sourceCode":"    cm ||= ContentMigration.where(id: migration_id).first\n    cm.save if cm.capture_job_id\n    cm.job_progress.start unless cm.skip_job_progress\n    begin\n      cm.update_conversion_progress(1)\n      settings = cm.migration_settings.clone\n      settings[:content_migration_id] = migration_id\n      settings[:user_id] = cm.user_id\n      settings[:content_migration] = cm\n      settings[:is_discussion_checkpoints_enabled] = discussion_checkpoints_enabled?(cm)\n\n      if cm.attachment\n        settings[:attachment_id] = cm.attachment.id\n      elsif settings[:file_url]\n        # create attachment and download file\n        att = Canvas::Migration::Worker.download_attachment(cm, settings[:file_url])\n        settings[:attachment_id] = att.id\n      elsif !settings[:no_archive_file]\n        raise Canvas::Migration::Error, I18n.t(:no_migration_file, \"File required for content migration.\")\n      end\n\n      converter_class = settings[:converter_class]\n      unless converter_class\n        if settings[:no_archive_file]\n          raise ArgumentError, \"converter_class required for content migration with no file\"\n        end\n\n        settings[:archive] = Canvas::Migration::Archive.new(settings)\n        converter_class = settings[:archive].get_converter\n      end\n      converter = converter_class.new(settings)\n\n      course = converter.export\n      export_folder_path = course[:export_folder_path]\n      overview_file_path = course[:overview_file_path]\n\n      if overview_file_path","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/cc/importer/cc_worker.rb#L22-L58","documentation":"CC::Importer::CCWorker#perform starts a content-migration conversion job. Before instantiating the converter it needs a source package: it looks for an attachment on the ContentMigration, then a file_url to download, and finally honors a no_archive_file escape hatch. If none of the three exist, it raises Canvas::Migration::Error with 'File required for content migration.' because there is no archive to convert.","triggerScenarios":"A ContentMigration is queued with no cm.attachment, no migration_settings[:file_url], and no migration_settings[:no_archive_file] — e.g. an upload request created the migration record but the attachment failed to attach, or an API client kicked off the job without posting the file.","commonSituations":"Multipart upload dropped the file before the job ran; a course-copy/import API call was made with neither a file nor a source_course; a background job was requeued after the attachment record was deleted; custom integrations calling the imports API without the export_file parameter.","solutions":["Re-create or re-upload the migration with a valid file attachment (check cm.attachment_id is set before enqueueing).","If migrating by URL, set migration_settings[:file_url] to a reachable archive URL so the worker can download it.","If the migration intentionally has no archive (e.g. copy by source course ID), set migration_settings[:no_archive_file] = true and supply migration_settings[:converter_class].","Inspect the failed ContentMigration's migration_settings to see which of attachment/file_url/no_archive_file was expected but missing."],"exampleFix":"# before\nmigration = course.content_migrations.create!\nmigration.migration_settings[:migration_type] = 'canvas_cartridge_importer'\nmigration.save!\nmigration.queue_migration\n\n# after\nmigration = course.content_migrations.create!\nmigration.attachment = uploaded_zip_attachment\nmigration.migration_settings[:migration_type] = 'canvas_cartridge_importer'\nmigration.save!\nmigration.queue_migration","handlingStrategy":"validation","validationCode":"raise 'attachment required' unless cm.attachment || cm.migration_settings[:file_url] || cm.migration_settings[:no_archive_file]\ncm.queue_migration","typeGuard":"def migration_has_source?(cm)\n  cm.attachment.present? || cm.migration_settings[:file_url].present? || cm.migration_settings[:no_archive_file] == true\nend","tryCatchPattern":"begin\n  worker.perform(cm)\nrescue Canvas::Migration::Error => e\n  if e.message.include?(\"File required\")\n    cm.fail_with_error!(e)\n  else\n    raise\n  end\nend","preventionTips":["Validate the migration has an attachment, file_url, or no_archive_file before enqueueing","In API integrations, assert the uploaded file attached successfully before returning 2xx","Watch for job requeues that may outlive the attachment record"],"tags":["ruby","content-migration","missing-file","background-job"],"backgroundTag":"empty-required-field","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"}