FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

A block with an uninstalled XBlock type permanently breaks the whole course's block structure · Issue #39131 · openedx/openedx-platform · GitHub

A block with an uninstalled XBlock type permanently breaks the whole course's block structure #39131

Description

Expected Behavior

A block whose XBlock type has no installed plugin is skipped during the block structure build. The rest of the course still renders.

Current Behavior

ContentLibraryTransformer.collect() calls XBlock.load_class() on every block during topological traversal, with nothing catching PluginMissingError:

https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/course_blocks/transformers/library_content.py#L73-L76

One unloadable block aborts the build for the entire course. Because the build fails, nothing is written to BlockStructureModel, the fallback path raises BlockStructureNotFound, and the next request repeats the identical failure. There is no cached structure to serve and no way to create one, so the course never recovers on its own.

We hit this in production on a course holding a block of type p, a bare <p> element promoted to block level by an OLX import:

PluginMissingError: p
  File "lms/djangoapps/course_blocks/transformers/library_content.py", line 73, in collect
    for block_key in block_structure.topological_traversal(
  File "openedx/core/lib/graph_traversals.py", line 329, in _traverse_generic
    should_yield_node = filter_func(current_node)
  File "lms/djangoapps/course_blocks/transformers/library_content.py", line 74, in <lambda>
    filter_func=lambda block_key: issubclass(XBlock.load_class(block_key.block_type), ItemBankMixin),
  File "xblock/plugin.py", line 158, in load_class
    raise PluginMissingError(identifier)

During handling of the above exception, another exception occurred:

BlockStructureNotFound: Block structure not found; data_usage_key: block-v1:ORG+NUMBER+RUN+type@course+block@course

Every learner-facing endpoint for that course returned 500 continuously for over 19 hours, at roughly 650 failures an hour:

  • /api/course_home/outline/
  • /api/courseware/course/
  • /api/course_home/dates/
  • /courses/…/progress
  • /api/discussion/v3/course_topics/
  • /api/courses/v1/blocks/
  • lms.djangoapps.grades.tasks.recalculate_subsection_grade_v3

update_course_in_cache_v2 fails on the same traversal, so the CMS cannot rebuild the structure either. We have seen the same crash on a separate course with PluginMissingError: ubcpi, so it is not specific to one bad block type.

Steps to Reproduce

  1. Get a course into the modulestore holding a block whose block_type has no installed XBlock, e.g. p from an OLX import where a bare <p> element sits at block level.
  2. Clear the block structure cache for that course.
  3. As an enrolled learner, request /api/course_home/outline/<course_id>.
  4. The request 500s with PluginMissingError. Every subsequent request repeats it.

Possible Solution

Route every XBlock.load_class call in the module through a helper that returns None on PluginMissingError, and have the collect() filter treat an unloadable type as "not an ItemBankMixin".

The module's other three load_class call sites already guard on block_class is None, but load_class raises rather than returning None, so those guards cannot fire today. The fix makes them live.

I have a patch with a regression test and will open a PR referencing this issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL