-
Notifications
You must be signed in to change notification settings - Fork 680
Description
What happened?
Description
We use environment variables to set the license keys for Craft itself, and for all commercial plugins:
CRAFT_LICENSE_KEY=[redacted]
PLUGIN_LICENSE_KEY_FORMIE=[redacted]
PLUGIN_LICENSE_KEY_RETOUR=[redacted]
Sometimes, new plugins are added during development. In this case, we add the new environment variable (without a value) to the .env.example
file. But not all environments (dev, staging or prod) will immediately have this variable in the .env
file.
If the environment variable is set in the .env
file, Craft creates a license key and writes it to that file when either Settings -> Plugins
or the Plugin Store are visited in the Control Panel. This is as expected and convenient.
The problem is what happens when the environment variable does not exist at all in the .env
file. In this case, Craft attempts to modify the project config to overwrite the environment variable with a hard-coded key. This leads to two problems:
Plugin store fails to display in production environments
In the production environment, the scenario above results in an error because the project config is read-only, and the plugin store fails to load:

2025-10-13 11:32:02 [web.ERROR] [yii\base\NotSupportedException] Changes to the project config are not possible while in read-only mode. {"trace":["#0 /path/to/project/vendor/craftcms/cms/src/services/ProjectConfig.php(526): craft\\services\\ProjectConfig->_setInternal()","#1 /path/to/project/vendor/craftcms/cms/src/services/Plugins.php(1193): craft\\services\\ProjectConfig->set()","#2 /path/to/project/vendor/craftcms/cms/src/helpers/Api.php(203): craft\\services\\Plugins->setPluginLicenseKey()","#3 /path/to/project/vendor/craftcms/cms/src/controllers/AppController.php(172): craft\\helpers\\Api::processResponseHeaders()","#4 [internal function]: craft\\controllers\\AppController->actionProcessApiResponseHeaders()","#5 /path/to/project/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array()","#6 /path/to/project/vendor/yiisoft/yii2/base/Controller.php(178): yii\\base\\InlineAction->runWithParams()","#7 /path/to/project/vendor/yiisoft/yii2/base/Module.php(552): yii\\base\\Controller->runAction()","#8 /path/to/project/vendor/craftcms/cms/src/web/Application.php(360): yii\\base\\Module->runAction()","#9 /path/to/project/vendor/craftcms/cms/src/web/Application.php(659): craft\\web\\Application->runAction()","#10 /path/to/project/vendor/craftcms/cms/src/web/Application.php(322): craft\\web\\Application->_processActionRequest()","#11 /path/to/project/vendor/yiisoft/yii2/base/Application.php(384): craft\\web\\Application->handleRequest()","#12 /path/to/project/web/index.php(10): yii\\base\\Application->run()","#13 {main}"],"memory":4056824,"exception":"[object] (yii\\base\\NotSupportedException(code: 0): Changes to the project config are not possible while in read-only mode. at /path/to/project/vendor/craftcms/cms/src/services/ProjectConfig.php:554)"}
2025-10-13 11:32:10 [web.WARNING] [craft\services\Updates::getUpdates] Couldn't get updates: Changes to the project config are not possible while in read-only mode. {"memory":4341480}
Unwanted changes to the project config
We very often get those unwanted changes to the project config where the licenseKey
for a plugin, which we have defined through an environment variable like $PLUGIN_LICENSE_KEY_FORMIE
, is replaced with a hardcoded license key. This happens every time a new commercial plugin is added, and any developer on the team forgets to add the new environment variable to their .env
file.
Steps to reproduce
- Install a commercial plugin and set the license key to an environment variable that exists in the
.env
. - Remove the environment variable from the
.env
file. - Open the plugin store in production mode.
Expected behavior
If a plugin license key is set to an environment variable, Craft should never attempt to replace that with a hardcoded key. It should continue to operate without a license key, or if that is not possible, explicitly throw an error that informs the user of the problem.
Actual behavior
See above.
Craft CMS version
5.8.18
PHP version
8.3
Operating system and version
No response
Database type and version
No response
Image driver and version
No response