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
- Enable authz flag: authz.enable_course_authoring.
- Log in to Studio with a user that does not have course creator access.
- Request course creator access from Studio.
- Log in as an admin user.
- Go to the course creator request in Django admin:
/admin/course_creators/coursecreator/<id>/change/
- Try to approve or grant the request.
- 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/
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_userswithCourseCreatorRole(). During the authz role assignment flow,authz_add_rolereceivescourse_key=Noneand tries to parse it as aCourseLocator.This raises:
Steps to reproduce
/admin/course_creators/coursecreator/<id>/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_roletries to parsecourse_key=Noneas aCourseLocator, which raises anInvalidKeyError.Relevant traceback
Obtained using portainer:
https://studio.verawood.demo.edly.io/admin/course_creators/coursecreator/1/change/