Course Outline error in lms

Going to course home fails since the outline API (/api/course_home/outline/course-v1:ORG+NUMBER+RUN) raises a 500 error. This issue is isolated to one course, while all other courses work as expected. Course loads ok in studio.

The error in the logs is as follows:

  File "/openedx/edx-platform/openedx/features/course_experience/utils.py", line 43, in populate_children
    child_detail = populate_children(all_blocks[child_id], all_blocks)
  [Previous line repeated 1 more time]
TypeError: unhashable type: 'ReturnDict'

I edited the populate_children function to see the type and the content of the child_id when used in all_blocks[child_id], so just added a print(i, type(child_id), child_id). This is what I get when hitting the outline API:

0 <class 'str'> block-v1:ORG+NUMBER+RUN+type@vertical+block@Lesson_2_Lab_Solution_Using_Essential_Tools
0 <class 'str'> block-v1:ORG+NUMBER+RUN+type@video+block@video_VIDEO_4ee74651793a
7 <class 'str'> block-v1:ORG+NUMBER+RUN+type@sequential+block@Lesson_3_Essential_File_Management_Tools
0 <class 'str'> block-v1:ORG+NUMBER+RUN+type@vertical+block@vertical_Learning_objectives_51c61ad4740b
0 <class 'rest_framework.utils.serializer_helpers.ReturnDict'> {'id': 'block-v1:ORG+NUMBER+RUN+type@video+block@video_VIDEO_4ee74651793a', 'block_id': 'video_VIDEO_4ee74651793a', 'lms_web_url': 'https://mylms.com/courses/course-v1:ORG+NUMBER+RUN/jump_to/block-v1:ORG+NUMBER+RUN+type@video+block@video_VIDEO_4ee74651793a', 'legacy_web_url': 'https://mylms.com/courses/course-v1:ORG+NUMBER+RUN/jump_to/block-v1:ORG+NUMBER+RUN+type@video+block@video_VIDEO_4ee74651793a?experience=legacy', 'student_view_url': 'https://mylms.com/xblock/block-v1:ORG+NUMBER+RUN+type@video+block@video_VIDEO_4ee74651793a', 'type': 'video', 'display_name': 'VIDEO', 'graded': False, 'has_score': False, 'completion': 0.0}

As the error had already pointed out, this last child_id could not be used since it is unhashable. All other courses that are ok return all child ids as strings.

Now I have these questions?

  • How could that course ended up in such state?
  • Why does it get that dict instead of the string?.
  • Has someone experience something similar?.

Workaround: We created a new course. Fortunately the course was under construction so there were no students enrolled.

Hi :wave:

I can answer this part at least. The populate_children function in question has a strange design where it recursively modifies the all_blocks array in-place, and what it does (per the docstring) is it “replaces each id in its children array with the full representation of that child”.

Where this breaks is with your video video_VIDEO_4ee74651793a which actually appears in the course twice (based on what I can tell from the log). So the first time the function sees that video, it replaces the ID with the full dictionary of data about the child. The second time, it encounters the data instead of the ID, and the error results.

If you export the course, remove the duplicate video, and re-import it, or something along those lines, you should be fine. But the root of the problem is this function which is using one field (children) for two totally different types of data - IDs and dicts. If you have time to fix it, I think the function should be modified to put the full data into a new field like children_details instead of re-using the same children field. Adding type hints would also help reveal and maybe avoid this problem.