Skip to content

Can't grant course creator when flag is enabled #353

Description

@mariajgrimaldi

Description

When a user requests course creator access from Studio, approving the request from Django admin fails with a 500 error. This was reproduced in Verawood when trying to grant course creator access from:

/admin/course_creators/coursecreator/1/change/

The error happens while saving the course creator request. The request triggers the course creator group update flow, which calls auth.add_users with CourseCreatorRole(). During the authz role assignment flow, authz_add_role receives course_key=None and tries to parse it as a CourseLocator.

This raises:

opaque_keys.InvalidKeyError: <class 'opaque_keys.edx.locator.CourseLocator'>: None

Steps to reproduce

  1. Enable authz flag: authz.enable_course_authoring.
  2. Log in to Studio with a user that does not have course creator access.
  3. Request course creator access from Studio.
  4. Log in as an admin user.
  5. Go to the course creator request in Django admin: /admin/course_creators/coursecreator/<id>/change/
  6. Try to approve or grant the request.
  7. Save the change.

Expected behavior

The course creator request should be saved successfully.

Actual behavior

The admin page returns a 500 error.

The traceback shows that authz_add_role tries to parse course_key=None as a CourseLocator, which raises an InvalidKeyError.

Relevant traceback

Obtained using portainer:

2026-07-03 10:10:25,696 ERROR 24 [django.request] [user 10] [ip 79.148.157.228] log.py:253 - Internal Server Error: /admin/course_creators/coursecreator/1/change/

Traceback (most recent call last):
  File "/openedx/venv/lib/python3.12/site-packages/opaque_keys/__init__.py", line 189, in from_string
    namespace, rest = cls._separate_namespace(serialized)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/opaque_keys/__init__.py", line 220, in _separate_namespace
    raise InvalidKeyError(cls, serialized)
opaque_keys.InvalidKeyError: <class 'opaque_keys.edx.locator.CourseLocator'>: None

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/openedx/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/pyenv/versions/3.12.13/lib/python3.12/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/contrib/admin/options.py", line 720, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 192, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 190, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/views/decorators/cache.py", line 80, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/contrib/admin/sites.py", line 246, in inner
    return view(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/contrib/admin/options.py", line 1988, in change_view
    return self.changeform_view(request, object_id, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 48, in _wrapper
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 192, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 190, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/contrib/admin/options.py", line 1844, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/django/contrib/admin/options.py", line 1895, in _changeform_view
    self.save_model(request, new_object, form, not add)
  File "/openedx/edx-platform/cms/djangoapps/course_creators/admin.py", line 112, in save_model
    obj.save()
  File "/openedx/venv/lib/python3.12/site-packages/django/db/models/base.py", line 902, in save
    self.save_base(
  File "/openedx/venv/lib/python3.12/site-packages/django/db/models/base.py", line 1023, in save_base
    post_save.send(
  File "/openedx/venv/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/edx-platform/cms/djangoapps/course_creators/models.py", line 89, in post_save_callback
    update_creator_state.send(
  File "/openedx/venv/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/edx-platform/cms/djangoapps/course_creators/admin.py", line 134, in update_creator_group_callback
    update_course_creator_group(kwargs['caller'], user, create_role)
  File "/openedx/edx-platform/cms/djangoapps/course_creators/views.py", line 48, in update_course_creator_group
    auth.add_users(caller, CourseCreatorRole(), user)
  File "/openedx/edx-platform/common/djangoapps/student/auth.py", line 294, in add_users
    role.add_users(*users)
  File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 524, in add_users
    self._authz_add_users(users)
  File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 493, in _authz_add_users
    authz_add_role(
  File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 54, in authz_add_role
    course_locator = CourseLocator.from_string(course_key)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/opaque_keys/__init__.py", line 198, in from_string
    return cls.deprecated_fallback._from_deprecated_string(serialized)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/openedx/venv/lib/python3.12/site-packages/opaque_keys/edx/locator.py", line 403, in _from_deprecated_string
    raise InvalidKeyError(cls, serialized)
opaque_keys.InvalidKeyError: <class 'opaque_keys.edx.locator.CourseLocator'>: None

[pid: 24|app: 0|req: 1333/3073] 172.18.0.6 () {66 vars in 3511 bytes} [Fri Jul  3 10:10:25 2026] POST /admin/course_creators/coursecreator/1/change/ => generated 8681 bytes in 37 msecs (HTTP/1.1 500) 10 headers in 626 bytes (1 switches on core 0)
  • Site: Verawood sandbox
  • URL: https://studio.verawood.demo.edly.io/admin/course_creators/coursecreator/1/change/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions