From a3cc78a37726cc9e49497d2eed8b99b16927ef14 Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Fri, 12 Dec 2025 22:17:02 +0530 Subject: [PATCH] fix: handle PluginMissingError while migrating legacy blocks to libraries v2 (#37732) Unhandled exception while migration legacy xblocks into new library stops the migration process abruptly causing following issues: * Components not being collected into Collections for successful migrations * Data being corrupted for already migrated blocks most likely due to incomplete transaction. --- cms/djangoapps/modulestore_migrator/tasks.py | 4 +++ .../modulestore_migrator/tests/test_tasks.py | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/cms/djangoapps/modulestore_migrator/tasks.py b/cms/djangoapps/modulestore_migrator/tasks.py index b4f24f149b..ad7c5c4842 100644 --- a/cms/djangoapps/modulestore_migrator/tasks.py +++ b/cms/djangoapps/modulestore_migrator/tasks.py @@ -41,6 +41,7 @@ from openedx_learning.api.authoring_models import ( ) from user_tasks.tasks import UserTask, UserTaskStatus from xblock.core import XBlock +from xblock.plugin import PluginMissingError from common.djangoapps.split_modulestore_django.models import SplitModulestoreCourseIndex from common.djangoapps.util.date_utils import DEFAULT_DATE_TIME_FORMAT, strftime_localized @@ -1164,6 +1165,9 @@ def _migrate_component( except libraries_api.IncompatibleTypesError as e: log.error(f"Error validating block for library {context.target_library_key}: {e}") return None, str(e) + except PluginMissingError as e: + log.error(f"Block type not supported in {context.target_library_key}: {e}") + return None, f"Invalid block type: {e}" component = authoring_api.create_component( context.target_package_id, component_type=component_type, diff --git a/cms/djangoapps/modulestore_migrator/tests/test_tasks.py b/cms/djangoapps/modulestore_migrator/tests/test_tasks.py index 9b4ac4130c..b2748f9ba6 100644 --- a/cms/djangoapps/modulestore_migrator/tests/test_tasks.py +++ b/cms/djangoapps/modulestore_migrator/tests/test_tasks.py @@ -1843,6 +1843,35 @@ class TestMigrateFromModulestore(ModuleStoreTestCase): f"Not a valid source context key: {invalid_key}. Source key must reference a course or a legacy library." ) + def test_migrate_component_with_fake_block_type(self): + """ + Test _migrate_component with with_fake_block_type + """ + source_key = self.course.id.make_usage_key("fake_block", "test_fake_block") + olx = '' + context = _MigrationContext( + existing_source_to_target_keys={}, + target_package_id=self.learning_package.id, + target_library_key=self.library.library_key, + source_context_key=self.course.id, + content_by_filename={}, + composition_level=CompositionLevel.Unit, + repeat_handling_strategy=RepeatHandlingStrategy.Skip, + preserve_url_slugs=True, + created_at=timezone.now(), + created_by=self.user.id, + ) + + result, reason = _migrate_component( + context=context, + source_key=source_key, + olx=olx, + title="test" + ) + + self.assertIsNone(result) + self.assertEqual(reason, "Invalid block type: fake_block") + def test_migrate_from_modulestore_nonexistent_modulestore_item(self): """ Test migration when modulestore item doesn't exist