No Code Attached Yet
avatar Ruud68
Ruud68
28 Aug 2026

com_joomlaupdater shares the minimum_stability variable in config.xml with com_installer minimum_stability viariable. This leads to incorrect update information for the Joomla updater

Steps to reproduce on Joomla 6.1.3:

  1. in the com_joomlaupdater set in options > Minimum Stability = stable
  2. in com_installer set in options > Minimum Extension Stability = development
  3. in com_installer view update click [Check For Updates]
  4. updates will be listed including all update with stability level 'Development' and higher
  5. goto com_joomlaupdater
  6. Although the Minimum Stability is set to Stable, it will prompt you to update to Joomla 6.2.0-beta

expected behavior:

com_joomlaupdater should NOT prompt you to update to the (in this case) beta1 version as the Minimum Stability level for Joomla updater is set to Stable.

Note:

the other way around works okay, so when setting the com_joomlaupdater to development and the com_installer Minimum Extension Level to stable, there are not beta / dev / rc versions of extension listed,

avatar Ruud68 Ruud68 - open - 28 Aug 2026
avatar Ruud68 Ruud68 - change - 28 Aug 2026
Labels Removed: ?
avatar joomla-cms-bot joomla-cms-bot - change - 28 Aug 2026
Labels Added: No Code Attached Yet
avatar joomla-cms-bot joomla-cms-bot - labeled - 28 Aug 2026
avatar brianteeman
brianteeman - comment - 28 Aug 2026

Same problem as #46865 ?

avatar Ruud68
Ruud68 - comment - 28 Aug 2026

The logic described there goes beyond my English skills. but your comment (#46865 (comment)) is (I think) what I reported here.


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/48336.

avatar Ruud68
Ruud68 - comment - 28 Aug 2026

The logic described there goes beyond my English skills. but your comment (#46865 (comment)) is (I think) what I reported here.


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/48336.

avatar Ruud68
Ruud68 - comment - 28 Aug 2026

Ok, so found the root cause:
When listing the extensions that need an upgrade, Joomla File updates (this is Joomla Core) are excluded in administrator\components\com_installer\src\Model\UpdateModel::getListQuery() but in UpdateModel::findUpdates() it is NOT excluded, so when you check for updates in com_installer it finds updates for ALL (including Joomla File) with the stability level set to 'Minimal Extension Stability' and caches that result. This cache is shared with com_joomlaupdater > this will then notify you of the update for Joomla core.

The fix is to also exclude the Joomla File update site in com_installer UpdateModel, just like getListQuery() does.

in file: administrator\components\com_installer\src\Model\UpdateModel, replace the following function:

    public function findUpdates($eid = 0, $cacheTimeout = 0, $minimumStability = Updater::STABILITY_STABLE)
    {
        if (!$eid) {
            $db      = $this->getDatabase();
            $coreEid = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id;

            $query = $db->createQuery()
                ->select($db->quoteName('extension_id'))
                ->from($db->quoteName('#__extensions'))
                ->where($db->quoteName('extension_id') . ' != :eid')
                ->bind(':eid', $coreEid, ParameterType::INTEGER);

            $db->setQuery($query);

            $eid = $db->loadColumn();
        }

        Updater::getInstance()->findUpdates($eid, $cacheTimeout, $minimumStability);

        return true;
    }

As the results are cached, you need to clear cache / refetch the updates in both com_installer and com_joomlaupdater

avatar joomdonation
joomdonation - comment - 28 Aug 2026

@Ruud68 Query #__extensions table will result too many extension IDs and many of these extensions do not have update sites available. I guess it will be more efficient if we query #__update_sites_extensions instead

However, making the change like this will change the outcome of a public method and could result in backward incompatible changes, so unsafe. Look at the find method from UpdateController, I think it will be safe if we can find a way to exclude the $coreEid from two process:

Not sure how difficult it is yet, but I think it will be more safe than modify findUpdates method directly as you suggested.

avatar Ruud68
Ruud68 - comment - 28 Aug 2026

@joomdonation , you are right. It is public so we must assume others are using it in their own code.

so the fix should then be in the UpdateController::find() and UpdateController::ajax() where we pass a computed list instead of a literal 0 for $eid.
For that computed list we create a UpdateModel::getExtensionIdsForUpdateCheck() that will return all extension IDs except Joomla File / Core.

in UpdateModel.php add:

    /**
     * Returns the IDs of all extensions that com_installer should check for updates, excluding the Joomla
     * core "extension". Joomla core's own update site must only ever be refreshed by com_joomlaupdate using
     * its own "minimum_stability" setting;
     *
     * @return  int[]
     *
     * @since   __DEPLOY_VERSION__
     */
    public function getExtensionIdsForUpdateCheck()
    {
        $db      = $this->getDatabase();
        $coreEid = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id;

        $query = $db->createQuery()
            ->select($db->quoteName('extension_id'))
            ->from($db->quoteName('#__extensions'))
            ->where($db->quoteName('extension_id') . ' != :eid')
            ->bind(':eid', $coreEid, ParameterType::INTEGER);

        $db->setQuery($query);

        return $db->loadColumn();
    }

in UpdateController.php change:
public function ajax()

        $model->findUpdates($findEid, $cache_timeout, $minimum_stability);

to

        $findEid = $eid ?: $model->getExtensionIdsForUpdateCheck();
        $model->findUpdates($findEid, $cache_timeout, $minimum_stability);

and, public function find()

        $model->findUpdates(0, $cache_timeout, $minimum_stability);

to

        $model->findUpdates($model->getExtensionIdsForUpdateCheck(), $cache_timeout, $minimum_stability);
avatar Ruud68
Ruud68 - comment - 28 Aug 2026

@joomdonation , you are right. It is public so we must assume others are using it in their own code.

so the fix should then be in the UpdateController::find() and UpdateController::ajax() where we pass a computed list instead of a literal 0 for $eid.
For that computed list we create a UpdateModel::getExtensionIdsForUpdateCheck() that will return all extension IDs except Joomla File / Core.

in UpdateModel.php add:

    /**
     * Returns the IDs of all extensions that com_installer should check for updates, excluding the Joomla
     * core "extension". Joomla core's own update site must only ever be refreshed by com_joomlaupdate using
     * its own "minimum_stability" setting;
     *
     * @return  int[]
     *
     * @since   __DEPLOY_VERSION__
     */
    public function getExtensionIdsForUpdateCheck()
    {
        $db      = $this->getDatabase();
        $coreEid = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id;

        $query = $db->createQuery()
            ->select('DISTINCT ' . $db->quoteName('extension_id'))
            ->from($db->quoteName('#__update_sites_extensions'))
            ->where($db->quoteName('extension_id') . ' != :eid')
            ->bind(':eid', $coreEid, ParameterType::INTEGER);

        $db->setQuery($query);

        return $db->loadColumn();
    }

in UpdateController.php change:
public function ajax()

        $model->findUpdates($findEid, $cache_timeout, $minimum_stability);

to

        $findEid = $eid ?: $model->getExtensionIdsForUpdateCheck();
        $model->findUpdates($findEid, $cache_timeout, $minimum_stability);

and, public function find()

        $model->findUpdates(0, $cache_timeout, $minimum_stability);

to

        $model->findUpdates($model->getExtensionIdsForUpdateCheck(), $cache_timeout, $minimum_stability);
avatar Ruud68
Ruud68 - comment - 28 Aug 2026

@joomdonation , you are right. It is public so we must assume others are using it in their own code.

so the fix should then be in the UpdateController::find() and UpdateController::ajax() where we pass a computed list instead of a literal 0 for $eid.
For that computed list we create a UpdateModel::getExtensionIdsForUpdateCheck() that will return all extension IDs except Joomla File / Core.

in UpdateModel.php add:

    /**
     * Returns the IDs of all extensions that com_installer should check for updates, excluding the Joomla
     * core "extension". Joomla core's own update site must only ever be refreshed by com_joomlaupdate using
     * its own "minimum_stability" setting;
     *
     * @return  int[]
     *
     * @since   __DEPLOY_VERSION__
     */
    public function getExtensionIdsForUpdateCheck()
    {
        $db      = $this->getDatabase();
        $coreEid = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id;

        $query = $db->createQuery()
            ->select('DISTINCT ' . $db->quoteName('extension_id'))
            ->from($db->quoteName('#__update_sites_extensions'))
            ->where($db->quoteName('extension_id') . ' != :eid')
            ->bind(':eid', $coreEid, ParameterType::INTEGER);

        $db->setQuery($query);

        return $db->loadColumn();
    }

and replace function purge() wit the code below:

    public function purge()
    {
        $db      = $this->getDatabase();
        $coreEid = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id;

        try {
            // Leave the Joomla core update cache alone; it is only ever managed by com_joomlaupdate
            // using its own "minimum_stability" setting (see getExtensionIdsForUpdateCheck() above).
            $query = $db->createQuery()
                ->delete($db->quoteName('#__updates'))
                ->where($db->quoteName('extension_id') . ' != :eid')
                ->bind(':eid', $coreEid, ParameterType::INTEGER);
            $db->setQuery($query);
            $db->execute();
        } catch (ExecutionFailureException) {
            $this->_message = Text::_('JLIB_INSTALLER_FAILED_TO_PURGE_UPDATES');

            return false;
        }

        // Find the update site(s) belonging to Joomla core so their last check timestamp is left alone.
        $query = $db->createQuery()
            ->select($db->quoteName('update_site_id'))
            ->from($db->quoteName('#__update_sites_extensions'))
            ->where($db->quoteName('extension_id') . ' = :eid')
            ->bind(':eid', $coreEid, ParameterType::INTEGER);
        $db->setQuery($query);
        $coreUpdateSiteIds = $db->loadColumn();

        // Reset the last update check timestamp for every other update site
        $query = $db->getQuery(true)
            ->update($db->quoteName('#__update_sites'))
            ->set($db->quoteName('last_check_timestamp') . ' = ' . $db->quote(0));

        if (!empty($coreUpdateSiteIds)) {
            $query->whereNotIn($db->quoteName('update_site_id'), $coreUpdateSiteIds);
        }

        $db->setQuery($query);
        $db->execute();

        // Clear the administrator cache
        $this->cleanCache('_system');

        $this->_message = Text::_('JLIB_INSTALLER_PURGED_UPDATES');

        return true;
    }

in UpdateController.php change:
public function ajax()

        $model->findUpdates($findEid, $cache_timeout, $minimum_stability);

to

        $findEid = $eid ?: $model->getExtensionIdsForUpdateCheck();
        $model->findUpdates($findEid, $cache_timeout, $minimum_stability);

and, public function find()

        $model->findUpdates(0, $cache_timeout, $minimum_stability);

to

        $model->findUpdates($model->getExtensionIdsForUpdateCheck(), $cache_timeout, $minimum_stability);

for the CLI, change in .\libraries\src\Console\CheckUpdatesCommand.php in function doExecute()

        $model->findUpdates();

to

        $model->findUpdates($model->getExtensionIdsForUpdateCheck());
avatar joomdonation
joomdonation - comment - 28 Aug 2026

@Ruud68 I think it is right solution except that:

avatar Ruud68
Ruud68 - comment - 28 Aug 2026

Updated the code in #48336 (comment)

The changing of the purge is IMO not related, if that needs improvement that should be handled separately

avatar joomdonation
joomdonation - comment - 28 Aug 2026

The changing of the purge is IMO not related, if that needs improvement that should be handled separately

It is actually related. Before, we also find update for Joomla core, so purge all updates are OK. Now, we do not find update for Joomla core anymore, so purge needs to exclude updates for Joomla core.

avatar Ruud68
Ruud68 - comment - 28 Aug 2026

ok, done in #48336 (comment)

also did the CLI part as that was also 'ignorent' on the joomla core / extensions difference

avatar joomdonation
joomdonation - comment - 28 Aug 2026

Looks great, thanks @Ruud68. Can you make PR with your propose changes so that people can test it and have the issue fixed? As it is a bug fix, we need to make PR for 5.4-dev branch.

avatar Ruud68
Ruud68 - comment - 28 Aug 2026

Thanks @joomdonation for the help and keeping me 'focussed' :).
This is as far as my contributions go, so I hope somebody else can pick this up and do the PR.

Add a Comment

Login with GitHub to post a comment