⚠️ This issue respects the following points: ⚠️
Bug description
Since #63701, SchemaChecker::getFindings() replays the migrations of disabled apps. applyDisabledMigrations() loads their migration files with require_once:
https://github.com/nextcloud/server/blob/6b9285768/lib/private/DB/SchemaChecker.php#L141-L150
If a disabled app declares a migration class that an enabled app has already declared (same app namespace), PHP stops with Cannot redeclare class. That is a compile-time fatal error, not a \Throwable, so the surrounding try { … } catch (\Throwable) never runs and the whole process dies with exit code 255.
Two commands are affected:
occ db:schema:check crashes instead of reporting.
occ upgrade finishes the upgrade ("Update successful", maintenance mode off), then crashes in postUpgradeCheck() → checkSchema(). Its docblock says the check is "informational only and never fails the upgrade", but here it does. In the official Docker image (and images built on it) the entrypoint runs occ upgrade, so the container exits and restarts. Orchestrators that wait for the container to become healthy (depends_on: service_healthy) give up and leave the dependent containers (web server, cron) unstarted. Result: the instance is down after every update until someone intervenes.
A real-world trigger is common: integration_google and its fork google_synchronization share the Google namespace (unsupported together, see nextcloud/integration_google#142, but people end up with both). Here the fork was disabled and still took every upgrade down. Removing it (occ app:remove --keep-data google_synchronization) makes both commands work again.
Steps to reproduce
- Nextcloud 35.0.1 (same code on
master).
- Install and enable
integration_google (4.4.0).
- Install
google_synchronization (4.1.0) and leave it disabled. Both ship lib/Migration/Version03001001Date20241111105515.php in namespace OCA\Google\Migration.
- Run
occ db:schema:check → PHP fatal, exit code 255.
- Or upgrade (observed on 35.0.0 → 35.0.1 with a Docker image built on the official one):
occ upgrade prints "Update successful", then dies with the same fatal.
Any two apps whose migration classes share a fully qualified name should reproduce it the same way.
Expected behavior
A disabled app must never be able to crash occ upgrade or occ db:schema:check. If its migration classes cannot be loaded safely (already declared), skip that app and report it as a non-blocking finding or warning.
One possible fix: before require_once, build the expected class name from the app namespace (appinfo/info.xml) and the file name, as MigrationService does. If class_exists($fqcn, false) is true, skip the app. Loading the files in a separate process would also isolate the fatal.
Nextcloud Server version
35 (35.0.1)
Operating system
Other (TrueNAS SCALE 25.04, Nextcloud app = Docker image built on the official one)
PHP engine version
PHP 8.5 (8.5.11)
Web server
Apache (supported) — from the image, behind an nginx reverse proxy
Database engine version
PostgreSQL (17.11)
Is this bug present after an update or on a fresh install?
Updated from a MINOR version (ex. 32.0.1 to 32.0.2)
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
What user-backends are you using?
Configuration report
Not included: the fatal happens before any configuration matters, and the reproduction only depends on the two apps above.
List of activated Apps
57 apps enabled; the relevant ones are integration_google 4.4.0 (enabled) and google_synchronization 4.1.0 (disabled).
Nextcloud Signing status
Not relevant (both apps are from the App Store, unmodified).
Nextcloud Logs
Upgrading nextcloud from 35.0.0.10 ...
Turned on maintenance mode
Updating database schema
Updated database
Starting code integrity check...
Finished code integrity check
Update successful
Turned off maintenance mode
Resetting log level
PHP Fatal error: Cannot redeclare class OCA\Google\Migration\Version03001001Date20241111105515 (previously declared in /var/www/html/custom_apps/integration_google/lib/Migration/Version03001001Date20241111105515.php:21) in /var/www/html/custom_apps/google_synchronization/lib/Migration/Version03001001Date20241111105515.php on line 20
Stack trace:
#0 /var/www/html/lib/private/DB/SchemaChecker.php(146): require_once()
#1 /var/www/html/lib/private/DB/SchemaChecker.php(53): OC\DB\SchemaChecker->applyDisabledMigrations('google_synchron...', Object(Doctrine\DBAL\Schema\Schema), Array)
#2 /var/www/html/core/Command/Upgrade.php(250): OC\DB\SchemaChecker->getFindings()
#3 /var/www/html/core/Command/Upgrade.php(241): OC\Core\Command\Upgrade->checkSchema(Object(Symfony\Component\Console\Output\ConsoleOutput))
#4 /var/www/html/core/Command/Upgrade.php(200): OC\Core\Command\Upgrade->postUpgradeCheck(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
...
#11 /var/www/html/occ(15): require_once('/var/www/html/c...')
#12 {main}
Additional info
occ db:schema:check alone reproduces the fatal (exit code 255) without changing anything. After removing the disabled fork it runs to completion.
Investigation and write-up assisted by Claude Code; the logs, code references and reproduction were checked on the affected instance.
Bug description
Since #63701,
SchemaChecker::getFindings()replays the migrations of disabled apps.applyDisabledMigrations()loads their migration files withrequire_once:https://github.com/nextcloud/server/blob/6b9285768/lib/private/DB/SchemaChecker.php#L141-L150
If a disabled app declares a migration class that an enabled app has already declared (same app namespace), PHP stops with
Cannot redeclare class. That is a compile-time fatal error, not a\Throwable, so the surroundingtry { … } catch (\Throwable)never runs and the whole process dies with exit code 255.Two commands are affected:
occ db:schema:checkcrashes instead of reporting.occ upgradefinishes the upgrade ("Update successful", maintenance mode off), then crashes inpostUpgradeCheck()→checkSchema(). Its docblock says the check is "informational only and never fails the upgrade", but here it does. In the official Docker image (and images built on it) the entrypoint runsocc upgrade, so the container exits and restarts. Orchestrators that wait for the container to become healthy (depends_on: service_healthy) give up and leave the dependent containers (web server, cron) unstarted. Result: the instance is down after every update until someone intervenes.A real-world trigger is common:
integration_googleand its forkgoogle_synchronizationshare theGooglenamespace (unsupported together, see nextcloud/integration_google#142, but people end up with both). Here the fork was disabled and still took every upgrade down. Removing it (occ app:remove --keep-data google_synchronization) makes both commands work again.Steps to reproduce
master).integration_google(4.4.0).google_synchronization(4.1.0) and leave it disabled. Both shiplib/Migration/Version03001001Date20241111105515.phpin namespaceOCA\Google\Migration.occ db:schema:check→ PHP fatal, exit code 255.occ upgradeprints "Update successful", then dies with the same fatal.Any two apps whose migration classes share a fully qualified name should reproduce it the same way.
Expected behavior
A disabled app must never be able to crash
occ upgradeorocc db:schema:check. If its migration classes cannot be loaded safely (already declared), skip that app and report it as a non-blocking finding or warning.One possible fix: before
require_once, build the expected class name from the app namespace (appinfo/info.xml) and the file name, asMigrationServicedoes. Ifclass_exists($fqcn, false)is true, skip the app. Loading the files in a separate process would also isolate the fatal.Nextcloud Server version
35 (35.0.1)
Operating system
Other (TrueNAS SCALE 25.04, Nextcloud app = Docker image built on the official one)
PHP engine version
PHP 8.5 (8.5.11)
Web server
Apache (supported) — from the image, behind an nginx reverse proxy
Database engine version
PostgreSQL (17.11)
Is this bug present after an update or on a fresh install?
Updated from a MINOR version (ex. 32.0.1 to 32.0.2)
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
What user-backends are you using?
Configuration report
Not included: the fatal happens before any configuration matters, and the reproduction only depends on the two apps above.
List of activated Apps
57 apps enabled; the relevant ones are
integration_google4.4.0 (enabled) andgoogle_synchronization4.1.0 (disabled).Nextcloud Signing status
Not relevant (both apps are from the App Store, unmodified).
Nextcloud Logs
Additional info
occ db:schema:checkalone reproduces the fatal (exit code 255) without changing anything. After removing the disabled fork it runs to completion.Investigation and write-up assisted by Claude Code; the logs, code references and reproduction were checked on the affected instance.