Course thumbnail being incorrectly set in imported courses

Hello everyone,

I have migrated some courses from an old Open edX installation to a newer one and it appears that the course thumbnails are not showing correctly on the LMS.

Specifically, the course thumbnails get set to asset-v1+course+id+something+type@asset+block@images_course_image.jpg which is an image that does not exist.

What’s even more weird with this behaviour are the following details:

  1. The course card image is correctly set in the course’s outline at the Studio. The image and the preview are the correct image the original course had.
  2. The course card image/thumbnail is not changed in the LMS no matter what you do. Even if you change the image from Studio the LMS will still try to show the incorrect images_course_image.jpg
  3. This only happens with imported courses, courses manually created from Studio can set and change their card images without any issues.

I have checked for caching issues and other factors that might cause this with no luck in fixing the issue.

The only way I have managed to resolve this is by going into the main MySQL database Open edX uses and modify the record that contains the course details in order to use the correct course card image, using a query similar to this:

UPDATE course_overviews_courseoverview SET course_image_url = '/asset-v1:my+course+id+type@asset+block@my_course_image.jpg' WHERE id = 'course-v1:my+course+id';

Any help or ideas would be greatly appreciated.

tutor, version 19.0.2

What version of Tutor did you migrate from?

Hello, I do not have the exact minor/patch version but I know the installation was based on Open edX Koa so we are talking about v11 of Tutor, maybe even before Tutor was even released because the installation documents I have from the previous platform have no tutor commands described at all.

Hmm, that’s a pretty big leap and I’m not sure where the problem might have occurred :\

Since this has to do with course import/export and is such a wide gap (Koa → Redwood), perhaps @dave has an insight?

I have the exact same issue importing from Koa master 3. Is there any way to update the image from Studio? As OP mentioned, this is not possible. The image remains the same even if you change it in Studio.

This is a long shot - if you have access to run Django management commands on your instance, could you try the ./manage.py cms simulate_publish --courses <your-course-ids> and see if that helps?

I tried running the command locally and got the following error:

2025-10-17 15:31:11,822 WARNING 1 [py.warnings] [user None] [ip None] warnings.py:110 - <frozen importlib._bootstrap>:1047: ImportWarning: _SixMetaPathImporter.find_spec() not found; falling back to find_module()

2025-10-17 15:31:11,823 WARNING 1 [py.warnings] [user None] [ip None] warnings.py:110 - <frozen importlib._bootstrap>:673: ImportWarning: _SixMetaPathImporter.exec_module() not found; falling back to load_module()

2025-10-17 15:31:11,832 WARNING 1 [py.warnings] [user None] [ip None] warnings.py:110 - /openedx/venv/lib/python3.11/site-packages/boto/plugin.py:40: DeprecationWarning: the imp module is deprecated in favour of importlib and slated for
removal in Python 3.12; see the module's documentation for alternative uses
  import imp

2025-10-17 15:31:12,679 INFO 1 [simulate_publish] [user None] [ip None] simulate_publish.py:186 - simulate_publish starting, dry-run=False, delay=0 seconds
2025-10-17 15:31:12,679 INFO 1 [simulate_publish] [user None] [ip None] simulate_publish.py:267 - 1 courses specified: course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs
2025-10-17 15:31:12,679 INFO 1 [simulate_publish] [user None] [ip None] simulate_publish.py:217 - Emitting course_published signal (1 of 1) for course course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs
2025-10-17 15:31:12,739 WARNING 1 [edx_toggles.toggles.internal.waffle.flag] [user None] [ip None] flag.py:79 - Flag 'cms.export_course_metadata' accessed without a request, which is likely in the context of a celery task.
2025-10-17 15:31:12,746 INFO 1 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:299 - Attempting to load CourseOverview for course course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs from modulestore.
2025-10-17 15:31:12,871 INFO 1 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:197 - Updating course overview for course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs.
2025-10-17 15:31:12,903 INFO 1 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:338 - Multiple CourseOverviews for course course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs requested simultaneously; will only save one.
2025-10-17 15:31:12,904 INFO 1 [openedx.core.djangoapps.content.course_overviews.signals] [user None] [ip None] signals.py:135 - Course start date changed: course=course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs previous=2030-01-01T00:00:00+00:00 new=2021-10-01T00:00:00+00:00
2025-10-17 15:31:12,909 INFO 1 [celery_utils.logged_task] [user None] [ip None] logged_task.py:25 - Task openedx.core.djangoapps.schedules.tasks.update_course_schedules[058bd8b7-1ea8-4b5e-b434-6f68e0d7fc90] submitted with arguments None, {'course_id': 'course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs', 'new_start_date_str': '2021-10-01T00:00:00+00:00', 'new_upgrade_deadline_str': None}
2025-10-17 15:31:12,910 ERROR 1 [django.dispatch] [user None] [ip None] dispatcher.py:213 - Error calling _listen_for_course_publish in Signal.send_robust() (No installed app with label 'credentials'.)
Traceback (most recent call last):
  File "/openedx/venv/lib/python3.11/site-packages/django_redis/client/default.py", line 445, in decode
    value = int(value)
            ^^^^^^^^^^
ValueError: invalid literal for int() with base 10: b'\x80\x04\x95O\x01\x00\x00\x00\x00\x00\x00\x8c\x15django.db.models.base\x94\x8c\x0emodel_unpickle\x94\x93\x94\x8c\x0bcredentials\x94\x8c\x14CredentialsApiConfig\x94\x86\x94\x85\x94R\x94}\x94(\x8c\x06

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/openedx/venv/lib/python3.11/site-packages/django/apps/registry.py", line 158, in get_app_config
    return self.app_configs[app_label]
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 'credentials'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/openedx/venv/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 211, in send_robust
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/edx-platform/openedx/core/djangoapps/content/course_overviews/signals.py", line 42, in _listen_for_course_publish
    _check_for_course_changes(previous_course_overview, updated_course_overview)
  File "/openedx/edx-platform/openedx/core/djangoapps/content/course_overviews/signals.py", line 90, in _check_for_course_changes
    _check_for_pacing_changes(previous_course_overview, updated_course_overview)
  File "/openedx/edx-platform/openedx/core/djangoapps/content/course_overviews/signals.py", line 157, in _check_for_pacing_changes
    COURSE_PACING_CHANGED.send(
  File "/openedx/venv/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 176, in send
    return [
           ^
  File "/openedx/venv/lib/python3.11/site-packages/django/dispatch/dispatcher.py", line 177, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/edx-platform/openedx/core/djangoapps/programs/signals.py", line 157, in handle_course_pacing_change
    if not is_credentials_enabled():
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/edx-platform/openedx/core/djangoapps/credentials/api.py", line 15, in is_credentials_enabled
    return CredentialsApiConfig.current().is_learner_issuance_enabled
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/config_models/models.py", line 133, in current
    cached_response = TieredCache.get_cached_response(cache_key)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/edx_django_utils/cache/utils.py", line 191, in get_cached_response
    django_cached_response = cls._get_cached_response_from_django_cache(key)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/edx_django_utils/cache/utils.py", line 262, in _get_cached_response_from_django_cache
    cached_value = django_cache.get(key, _CACHE_MISS)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django_redis/cache.py", line 92, in get
    value = self._get(key, default, version, client)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django_redis/cache.py", line 29, in _decorator
    return method(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django_redis/cache.py", line 99, in _get
    return self.client.get(key, default=default, version=version, client=client)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django_redis/client/default.py", line 265, in get
    return self.decode(value)
           ^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django_redis/client/default.py", line 452, in decode
    value = self._serializer.loads(value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django_redis/serializers/pickle.py", line 32, in loads
    return pickle.loads(value)
           ^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 2524, in model_unpickle
    model = apps.get_model(*model_id)
            ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django/apps/registry.py", line 208, in get_model
    app_config = self.get_app_config(app_label)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.11/site-packages/django/apps/registry.py", line 165, in get_app_config
    raise LookupError(message)
LookupError: No installed app with label 'credentials'.
2025-10-17 15:31:13,077 INFO 1 [openedx.core.djangoapps.course_date_signals.handlers] [user None] [ip None] handlers.py:125 - Extracting course dates for course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs
2025-10-17 15:31:13,078 INFO 1 [edx_when.api] [user None] [ip None] api.py:94 - Setting date for BlockUsageLocator(CourseLocator('sdjkfbsdjkfbj', 'sdjkfbsjk', 'sfkdjbs', None, None), 'course', 'course'), start, datetime.datetime(2021, 10, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x79a7b899ead0>)
2025-10-17 15:31:13,097 INFO 1 [celery_utils.logged_task] [user None] [ip None] logged_task.py:25 - Task lms.djangoapps.discussion.tasks.update_discussions_map[b425d163-c2bf-4b74-885f-4209ba37404e] submitted with arguments [{'course_id': 'course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs'}], None

after I enabled the credentials plugin and re-launched my platform, I run the command again and it finished with no errors and the following output:

2025-10-17 15:53:32,876 WARNING 1 [py.warnings] [user None] [ip None] warnings.py:110 - <frozen importlib._bootstrap>:1047: ImportWarning: _SixMetaPathImporter.find_spec() not found; falling back to find_module()

2025-10-17 15:53:32,878 WARNING 1 [py.warnings] [user None] [ip None] warnings.py:110 - <frozen importlib._bootstrap>:673: ImportWarning: _SixMetaPathImporter.exec_module() not found; falling back to load_module()

2025-10-17 15:53:32,882 WARNING 1 [py.warnings] [user None] [ip None] warnings.py:110 - /openedx/venv/lib/python3.11/site-packages/boto/plugin.py:40: DeprecationWarning: the imp module is deprecated in favour of importlib and slated for
removal in Python 3.12; see the module's documentation for alternative uses
  import imp

2025-10-17 15:53:33,611 INFO 1 [simulate_publish] [user None] [ip None] simulate_publish.py:186 - simulate_publish starting, dry-run=False, delay=0 seconds
2025-10-17 15:53:33,611 INFO 1 [simulate_publish] [user None] [ip None] simulate_publish.py:267 - 1 courses specified: course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs
2025-10-17 15:53:33,611 INFO 1 [simulate_publish] [user None] [ip None] simulate_publish.py:217 - Emitting course_published signal (1 of 1) for course course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs
2025-10-17 15:53:33,664 WARNING 1 [edx_toggles.toggles.internal.waffle.flag] [user None] [ip None] flag.py:79 - Flag 'cms.export_course_metadata' accessed without a request, which is likely in the context of a celery task.
2025-10-17 15:53:33,670 INFO 1 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:299 - Attempting to load CourseOverview for course course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs from modulestore.
2025-10-17 15:53:33,800 INFO 1 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:197 - Updating course overview for course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs.
2025-10-17 15:53:33,843 INFO 1 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:338 - Multiple CourseOverviews for course course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs requested simultaneously; will only save one.
2025-10-17 15:53:33,844 INFO 1 [openedx.core.djangoapps.content.course_overviews.signals] [user None] [ip None] signals.py:135 - Course start date changed: course=course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs previous=2030-01-01T00:00:00+00:00 new=2021-10-01T00:00:00+00:00
2025-10-17 15:53:33,859 INFO 1 [celery_utils.logged_task] [user None] [ip None] logged_task.py:25 - Task openedx.core.djangoapps.schedules.tasks.update_course_schedules[257a8cf6-10bb-4b74-bc10-aa0880234e29] submitted with arguments None, {'course_id': 'course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs', 'new_start_date_str': '2021-10-01T00:00:00+00:00', 'new_upgrade_deadline_str': None}
2025-10-17 15:53:33,863 INFO 1 [openedx.core.djangoapps.content.course_overviews.signals] [user None] [ip None] signals.py:203 - The certificates display behavior for course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs has changed from end to CertificatesDisplayBehaviors.EARLY_NO_INFO
2025-10-17 15:53:33,884 INFO 1 [openedx.core.djangoapps.course_date_signals.handlers] [user None] [ip None] handlers.py:125 - Extracting course dates for course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs
2025-10-17 15:53:33,899 INFO 1 [edx_when.api] [user None] [ip None] api.py:94 - Setting date for BlockUsageLocator(CourseLocator('sdjkfbsdjkfbj', 'sdjkfbsjk', 'sfkdjbs', None, None), 'course', 'course'), start, datetime.datetime(2021, 10, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x775507e1c890>)
2025-10-17 15:53:33,928 INFO 1 [celery_utils.logged_task] [user None] [ip None] logged_task.py:25 - Task lms.djangoapps.discussion.tasks.update_discussions_map[ca200f4c-ca15-453e-b672-c36d02b6c74d] submitted with arguments [{'course_id': 'course-v1:sdjkfbsdjkfbj+sdjkfbsjk+sfkdjbs'}], None

but it doesn’t seem to fix the problem, the wrong course thumbnail is still being shown.

@Retr0 Ah! I am sorry that didn’t help. I recently did a hard migration from Juniper to Sumac and ran into all sorts of issues. I had a similar issue of the images not showing up, but got fixed somewhere along the way. I was hoping the simulate_publish command was the one that did it.

I have another migration in a couple of weeks. If I run into the same issue again, I will share what I find.