Several problems after update

Hi everyone,

I have a selfhosted version of Frappe LMS that uses docker compose.

I’ve running this perfectly for a couple of months, using Frappe LMS 2.40.0 with the following docker compose

name: learning_prod_setup
services:
  backend:
    depends_on:
      configurator:
        condition: service_completed_successfully
        required: true
    image: ghcr.io/frappe/lms:stable
    networks:
      default: null
    platform: linux/amd64
    pull_policy: missing
    restart: unless-stopped
    volumes:
      - type: volume
        source: sites
        target: /home/frappe/frappe-bench/sites
        volume: {}
  configurator:
    command:
      - |
        ls -1 apps > sites/apps.txt; bench set-config -g db_host $$DB_HOST; bench set-config -gp db_port $$DB_PORT; bench set-config -g redis_cache "redis://$$REDIS_CACHE"; bench set-config -g redis_queue "redis://$$REDIS_QUEUE"; bench set-config -g redis_socketio "redis://$$REDIS_QUEUE"; bench set-config -gp socketio_port $$SOCKETIO_PORT;
    depends_on:
      db:
        condition: service_healthy
        required: true
      redis-cache:
        condition: service_started
        required: true
      redis-queue:
        condition: service_started
        required: true
    entrypoint:
      - bash
      - -c
    environment:
      DB_HOST: db
      DB_PORT: "3306"
      REDIS_CACHE: redis-cache:6379
      REDIS_QUEUE: redis-queue:6379
      SOCKETIO_PORT: "9000"
    image: ghcr.io/frappe/lms:stable
    networks:
      default: null
    platform: linux/amd64
    pull_policy: missing
    restart: on-failure
    volumes:
      - type: volume
        source: sites
        target: /home/frappe/frappe-bench/sites
        volume: {}
  cron:
    command:
      - daemon
      - --docker
    depends_on:
      scheduler:
        condition: service_started
        required: true
    image: mcuadros/ofelia:latest
    networks:
      default: null
    volumes:
      - type: bind
        source: /var/run/docker.sock
        target: /var/run/docker.sock
        read_only: true
        bind:
          create_host_path: true
  db:
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake
      - --skip-innodb-read-only-compressed
    environment:
      MYSQL_ROOT_PASSWORD: 13c3d936a
    healthcheck:
      test:
        - CMD-SHELL
        - mysqladmin ping -h localhost --password=13c3d936a
      interval: 1s
      retries: 20
    image: mariadb:10.6
    networks:
      default: null
    restart: unless-stopped
    volumes:
      - type: volume
        source: db-data
        target: /var/lib/mysql
        volume: {}
  frontend:
    command:
      - nginx-entrypoint.sh
    depends_on:
      backend:
        condition: service_started
        required: true
      websocket:
        condition: service_started
        required: true
    environment:
      BACKEND: backend:8000
      CLIENT_MAX_BODY_SIZE: 50m
      FRAPPE_SITE_NAME_HEADER: $$host
      PROXY_READ_TIMEOUT: "120"
      SOCKETIO: websocket:9000
      UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
      UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
      UPSTREAM_REAL_IP_RECURSIVE: "off"
    image: ghcr.io/frappe/lms:stable
    networks:
      default: null
    platform: linux/amd64
    ports:
      - mode: ingress
        target: 8080
        published: "8010"
        protocol: tcp
    pull_policy: missing
    restart: unless-stopped
    volumes:
      - type: volume
        source: sites
        target: /home/frappe/frappe-bench/sites
        volume: {}
  queue-long:
    command:
      - bench
      - worker
      - --queue
      - long,default,short
    depends_on:
      configurator:
        condition: service_completed_successfully
        required: true
    image: ghcr.io/frappe/lms:stable
    networks:
      default: null
    platform: linux/amd64
    pull_policy: missing
    restart: unless-stopped
    volumes:
      - type: volume
        source: sites
        target: /home/frappe/frappe-bench/sites
        volume: {}
  queue-short:
    command:
      - bench
      - worker
      - --queue
      - short,default
    depends_on:
      configurator:
        condition: service_completed_successfully
        required: true
    image: ghcr.io/frappe/lms:stable
    networks:
      default: null
    platform: linux/amd64
    pull_policy: missing
    restart: unless-stopped
    volumes:
      - type: volume
        source: sites
        target: /home/frappe/frappe-bench/sites
        volume: {}
  redis-cache:
    image: redis:6.2-alpine
    networks:
      default: null
    restart: unless-stopped
  redis-queue:
    image: redis:6.2-alpine
    networks:
      default: null
    restart: unless-stopped
    volumes:
      - type: volume
        source: redis-queue-data
        target: /data
        volume: {}
  scheduler:
    command:
      - bench
      - schedule
    depends_on:
      configurator:
        condition: service_completed_successfully
        required: true
    image: ghcr.io/frappe/lms:stable
    labels:
      ofelia.enabled: "true"
      ofelia.job-exec.datecron.command: bench --site all backup
      ofelia.job-exec.datecron.schedule: '@every 6h'
      ofelia.job-exec.datecron.user: frappe
    networks:
      default: null
    platform: linux/amd64
    pull_policy: missing
    restart: unless-stopped
    volumes:
      - type: volume
        source: sites
        target: /home/frappe/frappe-bench/sites
        volume: {}
  websocket:
    command:
      - node
      - /home/frappe/frappe-bench/apps/frappe/socketio.js
    depends_on:
      configurator:
        condition: service_completed_successfully
        required: true
    image: ghcr.io/frappe/lms:stable
    networks:
      default: null
    platform: linux/amd64
    pull_policy: missing
    restart: unless-stopped
    volumes:
      - type: volume
        source: sites
        target: /home/frappe/frappe-bench/sites
        volume: {}
networks:
  default:
    name: learning_prod_setup_default
volumes:
  db-data:
    name: learning_prod_setup_db-data
  redis-queue-data:
    name: learning_prod_setup_redis-queue-data
  sites:
    name: learning_prod_setup_sites
x-backend-defaults:
  depends_on:
    configurator:
      condition: service_completed_successfully
  image: ghcr.io/frappe/lms:stable
  pull_policy: missing
  restart: unless-stopped
  volumes:
    - sites:/home/frappe/frappe-bench/sites
x-customizable-image:
  image: ghcr.io/frappe/lms:stable
  pull_policy: missing
  restart: unless-stopped
x-depends-on-configurator:
  depends_on:
    configurator:
      condition: service_completed_successfully

However, I had some bugs and due to the recents vulnerabilities discovered in React/NextJS, i wanted to updated the platform.

To do so, i simply performed a docker pull of the latest stable image and recreated the images.

However, I’m now facing several errors that prevent me from using the platform.

  • Once I log into the desktop app, I’m getting the following error
### App Versions
```
{
	"frappe": "15.99.0",
	"lms": "2.44.0"
}
```
### Route
```
Workspaces/LMS
```
### Traceback
```
Traceback (most recent call last):
  File "apps/frappe/frappe/app.py", line 120, in application
    response = frappe.api.handle(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/api/__init__.py", line 52, in handle
    data = endpoint(**arguments)
           ^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/api/v1.py", line 40, in handle_rpc_call
    return frappe.handler.handle()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/handler.py", line 53, in handle
    data = execute_cmd(cmd)
           ^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/handler.py", line 86, in execute_cmd
    return frappe.call(method, **frappe.form_dict)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/__init__.py", line 1754, in call
    return fn(*args, **newargs)
           ^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/utils/typing_validations.py", line 32, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 57, in getdoc
    get_docinfo(doc)
  File "apps/frappe/frappe/utils/typing_validations.py", line 32, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 108, in get_docinfo
    all_communications = _get_communications(doc.doctype, doc.name, limit=21)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 265, in _get_communications
    communications = get_communication_data(doctype, name, start, limit)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 328, in get_communication_data
    return frappe.db.sql(
           ^^^^^^^^^^^^^^
  File "apps/frappe/frappe/database/database.py", line 230, in sql
    self._cursor.execute(query, values)
  File "env/lib/python3.11/site-packages/pymysql/cursors.py", line 153, in execute
    result = self._query(query)
             ^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pymysql/cursors.py", line 322, in _query
    conn.query(q)
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 563, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 825, in _read_query_result
    result.read()
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 1199, in read
    first_packet = self.connection._read_packet()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 775, in _read_packet
    packet.raise_for_error()
  File "env/lib/python3.11/site-packages/pymysql/protocol.py", line 219, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "env/lib/python3.11/site-packages/pymysql/err.py", line 150, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.OperationalError: (1054, "Unknown column 'tabCommunication Link.communication_date' in 'ORDER BY'")

```
### Request Data
```
{
	"type": "GET",
	"args": {
		"doctype": "Dashboard Chart",
		"name": "New Signups"
	},
	"headers": {},
	"error_handlers": {},
	"url": "/api/method/frappe.desk.form.load.getdoc",
	"request_id": null
}
```
### Response Data
```
{
	"exception": "pymysql.err.OperationalError: (1054, \"Unknown column 'tabCommunication Link.communication_date' in 'ORDER BY'\")",
	"exc_type": "OperationalError"
}
```
  • I can’t even create an incription for a user in a course, as I get the following error
### App Versions
```
{
	"frappe": "15.99.0",
	"lms": "2.44.0"
}
```
### Route
```
Form/LMS Enrollment/new-lms-enrollment-jvyivjebdg
```
### Traceback
```
Traceback (most recent call last):
  File "apps/frappe/frappe/app.py", line 120, in application
    response = frappe.api.handle(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/api/__init__.py", line 52, in handle
    data = endpoint(**arguments)
           ^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/api/v1.py", line 40, in handle_rpc_call
    return frappe.handler.handle()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/handler.py", line 53, in handle
    data = execute_cmd(cmd)
           ^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/handler.py", line 86, in execute_cmd
    return frappe.call(method, **frappe.form_dict)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/__init__.py", line 1754, in call
    return fn(*args, **newargs)
           ^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/utils/typing_validations.py", line 32, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 57, in getdoc
    get_docinfo(doc)
  File "apps/frappe/frappe/utils/typing_validations.py", line 32, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 108, in get_docinfo
    all_communications = _get_communications(doc.doctype, doc.name, limit=21)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 265, in _get_communications
    communications = get_communication_data(doctype, name, start, limit)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/desk/form/load.py", line 328, in get_communication_data
    return frappe.db.sql(
           ^^^^^^^^^^^^^^
  File "apps/frappe/frappe/database/database.py", line 230, in sql
    self._cursor.execute(query, values)
  File "env/lib/python3.11/site-packages/pymysql/cursors.py", line 153, in execute
    result = self._query(query)
             ^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pymysql/cursors.py", line 322, in _query
    conn.query(q)
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 563, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 825, in _read_query_result
    result.read()
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 1199, in read
    first_packet = self.connection._read_packet()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pymysql/connections.py", line 775, in _read_packet
    packet.raise_for_error()
  File "env/lib/python3.11/site-packages/pymysql/protocol.py", line 219, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "env/lib/python3.11/site-packages/pymysql/err.py", line 150, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.OperationalError: (1054, "Unknown column 'tabCommunication Link.communication_date' in 'ORDER BY'")

```
### Request Data
```
{
	"type": "GET",
	"args": {
		"doctype": "Dashboard Chart",
		"name": "New Signups"
	},
	"headers": {},
	"error_handlers": {},
	"url": "/api/method/frappe.desk.form.load.getdoc",
	"request_id": null
}
```
### Response Data
```
{
	"exception": "pymysql.err.OperationalError: (1054, \"Unknown column 'tabCommunication Link.communication_date' in 'ORDER BY'\")",
	"exc_type": "OperationalError"
}
```

I guess this is because there are some migrations that have not been applied or something like that, but i can’t manage to fix them.

Can somebody help me? I can’t even get my previous version working anymore.

What would the best way to fix this?

Best regards,

Javier

I would assume that you did not run a “bench migrate”

docker-compose exec backend bash
bench --site mysite.com migrate

See the general documentation here.

That seemed to fix some of the problems. However, some of our users are not able to complete their quizes right now, and after impersonaing some of them, we have detected the following error:

Error: /api/method/lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary PermissionError Sin permiso para Inscripción LMS that returns a 403 error code

This used to work perfectly, and only started failing after the update

This looks like a missed migration after the image update. The Unknown column ... communication_date error is a schema mismatch, and the Frappe docs say bench migrate is what applies app patches and synchronizes database schema after pulling updates. In the thread itself, another user suggested bench --site mysite.com migrate, and the poster said that fixed some of the problems.

For the remaining quiz issue, the 403 and Sin permiso para Inscripción LMS message looks like a permissions regression on the LMS Enrollment DocType, so after running migrate it is worth checking role permissions for the affected users and clearing cache/restarting the bench. If the site still behaves inconsistently after a clean migrate, then I would inspect whether all app patches actually completed and whether any custom roles or DocType permissions were overwritten during the upgrade if after migration you still face table errors or suspect corruption, then tools like Stellar Repair for MySQL can help recover data, otherwise stick to fixing migrations.