Issue: Problems while building Tutor image with custom changes in Open edX platform

Hello, I am encountering an issue while trying to build a custom Tutor image for Open edX. I’m running the command:
tutor images build openedx-dev

However, during the build process, I get an error at the stage of npm run compile-sass -- --skip-themes:
ModuleNotFoundError: No module named ‘_sass’

I have already tried the following solutions:

  1. Running npm install sass@v1.54.8
  2. Running pip install libsass

These did not resolve the problem. My goal is to mount my local edx-platform directory to Tutor so I can start customizing Open edX, but I keep running into issues.

How can I properly mount the edx-platform to Tutor and build the image for customization without running into these Sass-related errors?

This started happening when I first tried mounting the edx-platform with Tutor before making any changes. Once I did this, the errors started appearing right away.

My goal is to mount my local edx-platform directory to Tutor so I can start customizing Open edX, but I keep running into these Sass-related errors.

I’ve referred to the documentation, but I’m stuck at this point. If anyone has experience in working with Open edX purely as a developer and could suggest the right way to mount the edx-platform and customize the image without these errors, I’d really appreciate it!

Any ideas or experience with a similar setup would be helpful!

I faced the same issue today, and I executed npm install command in edx-platform directory.
and tried again building openedx-dev image using command tutor images build openedx-dev and this time it works.

I believe it has some issue while syncing docker volume, I am not exactly sure about the issue. I will still do my investigation around the issue and post here If I find any more helpful information.

1 Like

Something like this happened to me in January because I had been following the latest changes on the main branch of tutor, and there was some change in how the dockerfile installs requirements. So if this happened after you pulled the latest updates from tutor, try running tutor config save before building the image.

1 Like

I’m encountering an issue with tutor dev start after previously mounting the edx-platform and successfully building the the openedx image.

  1. I had a tutor dev environment fully operational, accessible through ports 8000 and 8001.
  2. I followed the previous advice I received (thank you for that!), but now when I run tutor dev start, it fails.
  3. I’m seeing the following logs (I will paste the logs below).

My suspicion is that perhaps the ports (8000 and 8001) are still in use from the previous launch, even though I’m trying to start a new instance.

Could anyone provide guidance on how to resolve this? Is there a recommended way to properly shut down the previous tutor dev launch before running tutor dev start again? Or is there another problem i am overlooking?


(pro) linux@Yashaswi:~/dev/edx-platform$ tutor dev start
ℹ️  To exit logs without stopping the containers, use ctrl+z
docker compose -f /home/linux/.local/share/tutor/env/local/docker-compose.yml -f /home/linux/.local/share/tutor/env/local/docker-compose.prod.yml --project-name tutor_local stop
docker compose -f /home/linux/.local/share/tutor/env/local/docker-compose.yml -f /home/linux/.local/share/tutor/env/dev/docker-compose.yml --project-name tutor_dev up --remove-orphans
[+] Running 10/10
 ✔ Container tutor_dev-mysql-1        Running                                                                                               0.0s
 ✔ Container tutor_dev-watchthemes-1  Running                                                                                               0.0s
 ✔ Container tutor_dev-smtp-1         Running                                                                                               0.0s
 ✔ Container tutor_dev-permissions-1  Created                                                                                               0.0s
 ✔ Container tutor_dev-mongodb-1      Running                                                                                               0.0s
 ✔ Container tutor_dev-meilisearch-1  Running                                                                                               0.0s
 ✔ Container tutor_dev-redis-1        Running                                                                                               0.0s
 ✔ Container tutor_dev-cms-1          Recreated                                                                                            21.9s
 ✔ Container tutor_dev-mfe-1          Created                                                                                               1.2s
 ✔ Container tutor_dev-lms-1          Recreated                                                                                            11.0s
Attaching to cms-1, lms-1, meilisearch-1, mfe-1, mongodb-1, mysql-1, permissions-1, redis-1, smtp-1, watchthemes-1
permissions-1  | /mounts/lms already owned by 1000
permissions-1  | /mounts/cms already owned by 1000
permissions-1  | /mounts/openedx already owned by 1000
permissions-1  | /mounts/meilisearch already owned by 1000
permissions-1  | /mounts/mongodb already owned by 999
permissions-1  | /mounts/mysql already owned by 999
permissions-1  | /mounts/redis already owned by 1000
permissions-1 exited with code 0
mfe-1          | {"level":"info","ts":1740734482.3087137,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
mfe-1          | {"level":"warn","ts":1740734482.316594,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":2}
mfe-1          | {"level":"info","ts":1740734482.3222775,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
mfe-1          | {"level":"info","ts":1740734482.3232682,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000673500"}
mfe-1          | {"level":"info","ts":1740734482.3263466,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
mfe-1          | {"level":"info","ts":1740734482.327149,"logger":"tls","msg":"cleaning storage unit","description":"FileStorage:/data/caddy"}
mfe-1          | {"level":"info","ts":1740734482.328553,"logger":"tls","msg":"finished cleaning storage units"}
mfe-1          | {"level":"info","ts":1740734482.3295329,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
mfe-1          | {"level":"info","ts":1740734482.3295832,"msg":"serving initial configuration"}
cms-1          | Traceback (most recent call last):
cms-1          |   File "/openedx/edx-platform/./manage.py", line 103, in <module>
cms-1          |     startup.run()
cms-1          |   File "/openedx/edx-platform/cms/startup.py", line 20, in run
cms-1          |     django.setup()
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/django/__init__.py", line 24, in setup
cms-1          |     apps.populate(settings.INSTALLED_APPS)
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/django/apps/registry.py", line 116, in populate
cms-1          |     app_config.import_models()
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/django/apps/config.py", line 269, in import_models
cms-1          |     self.models_module = import_module(models_module_name)
cms-1          |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1          |   File "/opt/pyenv/versions/3.11.8/lib/python3.11/importlib/__init__.py", line 126, in import_module
cms-1          |     return _bootstrap._gcd_import(name[level:], package, level)
cms-1          |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1          |   File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
cms-1          |   File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
cms-1          |   File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
cms-1          |   File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
cms-1          |   File "<frozen importlib._bootstrap_external>", line 940, in exec_module
cms-1          |   File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
cms-1          |   File "/openedx/edx-platform/common/djangoapps/entitlements/models.py", line 21, in <module>
cms-1          |     from lms.djangoapps.certificates import api as certificates_api
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/certificates/api.py", line 28, in <module>
cms-1          |     from lms.djangoapps.certificates.generation_handler import generate_certificate_task as _generate_certificate_task
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/certificates/generation_handler.py", line 24, in <module>
cms-1          |     from lms.djangoapps.grades.api import CourseGradeFactory
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/grades/api.py", line 15, in <module>
cms-1          |     from lms.djangoapps.grades import constants, context, course_data, events
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/grades/context.py", line 10, in <module>
cms-1          |     from .course_grade import CourseGrade
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/grades/course_grade.py", line 17, in <module>
cms-1          |     from .subsection_grade import ZeroSubsectionGrade
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/grades/subsection_grade.py", line 12, in <module>
cms-1          |     from lms.djangoapps.grades.models import BlockRecord, PersistentSubsectionGrade
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/grades/models.py", line 30, in <module>
cms-1          |     from lms.djangoapps.grades import events  # lint-amnesty, pylint: disable=unused-import
cms-1          |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1          |   File "/openedx/edx-platform/lms/djangoapps/grades/events.py", line 30, in <module>
cms-1          |     from openedx.features.enterprise_support.context import get_enterprise_event_context
cms-1          |   File "/openedx/edx-platform/openedx/features/enterprise_support/context.py", line 4, in <module>
cms-1          |     from enterprise.models import EnterpriseCourseEnrollment
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/enterprise/models.py", line 43, in <module>
cms-1          |     from enterprise import utils
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/enterprise/utils.py", line 115, in <module>
cms-1          |     from common.djangoapps.third_party_auth.provider import Registry
cms-1          |   File "/openedx/edx-platform/common/djangoapps/third_party_auth/provider.py", line 10, in <module>
cms-1          |     from common.djangoapps.third_party_auth.models import (
cms-1          |   File "/openedx/edx-platform/common/djangoapps/third_party_auth/models.py", line 50, in <module>
cms-1          |     _PSA_BACKENDS = {backend_class.name: backend_class for backend_class in _load_backend_classes()}
cms-1          |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1          |   File "/openedx/edx-platform/common/djangoapps/third_party_auth/models.py", line 50, in <dictcomp>
cms-1          |     _PSA_BACKENDS = {backend_class.name: backend_class for backend_class in _load_backend_classes()}
cms-1          |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1          |   File "/openedx/edx-platform/common/djangoapps/third_party_auth/models.py", line 47, in _load_backend_classes
cms-1          |     auth_class = module_member(class_path)
cms-1          |                  ^^^^^^^^^^^^^^^^^^^^^^^^^
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/social_core/utils.py", line 59, in module_member
cms-1          |     module = import_module(mod)
cms-1          |              ^^^^^^^^^^^^^^^^^^
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/social_core/utils.py", line 53, in import_module
cms-1          |     __import__(name)
cms-1          |   File "/openedx/edx-platform/openedx/core/djangoapps/content_libraries/auth.py", line 13, in <module>
cms-1          |     from .models import LtiProfile
cms-1          |   File "/openedx/edx-platform/openedx/core/djangoapps/content_libraries/models.py", line 84, in <module>
cms-1          |     class ContentLibrary(models.Model):
cms-1          |   File "/openedx/venv/lib/python3.11/site-packages/django/db/models/base.py", line 134, in __new__
cms-1          |     raise RuntimeError(
cms-1          | RuntimeError: Model class openedx.core.djangoapps.content_libraries.models.ContentLibrary doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.