I have recently migrated my Seafile instance from the Community Edition to the Professional Edition by updating the Docker image. While the main functionalities appear to be working, I’ve encountered a bug when trying to modify a user’s active status.
When I attempt to activate a user through the admin panel, the action triggers SQL errors in the seaf-server log. The errors indicate that two tables, UserUploadRateLimit and UserDownloadRateLimit, are missing from the seafile_db database.
It seems that the database schema was not fully updated during the migration from the Community to the Professional version. This issue is similar to previously reported problems where migrations to the Pro version resulted in other missing tables.
Steps to Reproduce
Set up a Seafile instance using the Community Edition Docker image.
Populate it with some user data.
Stop the instance and change the Docker image to the latest Seafile Professional Edition tag.
Restart the server to initiate the upgrade.
Log in as an administrator.
Navigate to the user administration page.
Attempt to change the status of any user from “active” to “inactive” and then vice versa.
Check the logs for the seafile container.
Expected Behavior
The user’s active status should be updated successfully without any errors being generated in the server logs.
Logs
Here are the relevant log entries that appear each time the action is attempted.
seafile-1 | 2025-10-18T21:02:09.837423000Z [seaf-server] [2025-10-18 17:02:09] [WARNING] ../common/seaf-db-mysql.c(239): Failed to prepare sql select upload_limit from UserUploadRateLimit where user=?: Table 'seafile_db.UserUploadRateLimit' doesn't exist
seafile-1 | 2025-10-18T21:02:09.837850000Z [seaf-server] [2025-10-18 17:02:09] [WARNING] ../common/seaf-db-mysql.c(239): Failed to prepare sql SELECT download_limit from UserDownloadRateLimit WHERE user=?: Table 'seafile_db.UserDownloadRateLimit' doesn't exist
seafile-1 | 2025-10-18T21:02:36.391973000Z [seaf-server] [2025-10-18 17:02:36] [WARNING] ../common/seaf-db-mysql.c(239): Failed to prepare sql select upload_limit from UserUploadRateLimit where user=?: Table 'seafile_db.UserUploadRateLimit' doesn't exist
seafile-1 | 2025-10-18T21:02:36.393384000Z [seaf-server] [2025-10-18 17:02:36] [WARNING] ../common/seaf-db-mysql.c(239): Failed to prepare sql SELECT download_limit from UserDownloadRateLimit WHERE user=?: Table 'seafile_db.UserDownloadRateLimit' doesn't exist
seafile-1 | 2025-10-18T21:02:49.061507000Z [seaf-server] [2025-10-18 17:02:49] [WARNING] ../common/seaf-db-mysql.c(239): Failed to prepare sql select upload_limit from UserUploadRateLimit where user=?: Table 'seafile_db.UserUploadRateLimit' doesn't exist
seafile-1 | 2025-10-18T21:02:49.062240000Z [seaf-server] [2025-10-18 17:02:49] [WARNING] ../common/seaf-db-mysql.c(239): Failed to prepare sql SELECT download_limit from UserDownloadRateLimit WHERE user=?: Table 'seafile_db.UserDownloadRateLimit' doesn't exist
Environment:
Seafile Version: Seafile 13.0.11 Professional Edition
I used AI to compare my database table structure with the /opt/seafile/seafile-server-latest/sql/mysql/seafile.sql and /opt/seafile/seafile-server-latest/sql/mysql/ccnet.sql, then AI generated this code to fix. Hope this will helps others.
-- ====================================================================
-- Seafile Pro Upgrade Fix Script
-- Fixes missing columns and tables after upgrading from CE to Pro.
-- ====================================================================
--
-- Section 1: Fixes for the `ccnet_db` database
--
USE `ccnet_db`;
-- Add the missing 'is_department_owner' column to the EmailUser table.
-- The ALTER TABLE command might fail if the column already exists, which is safe to ignore.
ALTER TABLE `EmailUser` ADD COLUMN `is_department_owner` BOOL NOT NULL DEFAULT 0;
ALTER TABLE `EmailUser` ADD INDEX `idx_is_department_owner` (`is_department_owner`);
-- ====================================================================
--
-- Section 2: Fixes for the `seafile_db` database
--
USE `seafile_db`;
-- Create missing tables required by the Professional Edition.
-- Using 'CREATE TABLE IF NOT EXISTS' makes it safe to run even if some tables already exist.
CREATE TABLE IF NOT EXISTS `RoleUploadRateLimit` (
`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`role` VARCHAR(255),
`upload_limit` BIGINT,
UNIQUE INDEX(`role`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `RoleDownloadRateLimit` (
`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`role` VARCHAR(255),
`download_limit` BIGINT,
UNIQUE INDEX(`role`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `UserUploadRateLimit` (
`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`user` VARCHAR(255),
`upload_limit` BIGINT,
UNIQUE INDEX(`user`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `UserDownloadRateLimit` (
`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`user` VARCHAR(255),
`download_limit` BIGINT,
UNIQUE INDEX(`user`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `OrgUserDefaultQuota` (
`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`org_id` INTEGER,
`quota` BIGINT,
UNIQUE INDEX(`org_id`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `OrgDownloadRateLimit` (
`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`org_id` INTEGER,
`download_limit` BIGINT,
UNIQUE INDEX(`org_id`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `OrgUploadRateLimit` (
`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`org_id` INTEGER,
`upload_limit` BIGINT,
UNIQUE INDEX(`org_id`)
) ENGINE=INNODB;
-- ====================================================================
-- End of script.
-- ====================================================================
The consolidated SQL script worked perfectly, and all the Table doesn’t exist errors in the logs are now gone.
However, I’ve noticed a new UI-related issue that might have been hidden by the database errors. When I change a user’s status from ‘inactive’ to ‘active’ in the admin panel:
The backend operation succeeds. After I manually refresh the page, the user’s status is correctly updated.
But the frontend XHR request hangs indefinitely. The UI never receives a success response and remains in a loading state until I refresh the page.
So, while the database schema is now correct, there seems to be a separate issue where the API call for updating a user’s status fails to return a response to the browser.
Never mind, I’ve found the root cause of the hanging UI.
The issue was my server’s email sending configuration. It seems the user activation process tries to send a notification email, and the request hangs when the email service isn’t properly configured.
After I fixed my email settings, the UI works perfectly. It would be helpful if the UI could handle this email-sending failure more gracefully instead of hanging, as there were no errors in the server logs to indicate the problem.
This happens because the Community → Pro upgrade did not run the full Pro database migrations, so the rate-limit tables were never created. The fix is to apply the Pro schema manually: stop Seafile, exec into the Pro container, and run the bundled MySQL upgrade script for your version (for example the sql/mysql/*.sql files that include UserUploadRateLimit and UserDownloadRateLimit) against seafile_db, then restart the container. After those tables exist, changing a user’s active status works normally. If the schema update fails or the DB looks inconsistent after the migration, repairing the database first with some third party tools like Stellar Repair for MySQL and then reapplying the Pro schema is a safe way to recover.