PR-5.4-dev Pending

User tests: Successful: Unsuccessful:

avatar wakqasahmed
wakqasahmed
12 Sep 2026

Pull Request resolves #48034.

  • I read the Generative AI policy and my contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later.

Summary of Changes

updateManifestCaches() in administrator/components/com_admin/script.php queries every core extension (by type/element/folder/client_id) and calls Installer::refreshManifestCache() on each, reporting a "Error on updating manifest cache" message for any that return false.

Installer::refreshManifestCache() deliberately aborts and returns false when an extension's state == -1 (its own translated message, JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE, exists specifically for this) — that's intentional, not a real failure. But updateManifestCaches() has no way to tell an intentional no-op from a genuine problem, so any core extension that ends up at state = -1 (as the reporter saw for phpass and plg_quickicon_eos after a 3.x → 6.1.1 migration) always surfaces a spurious error during every subsequent update or reinstall.

A state = -1 extension is not "disabled" — that's a separate enabled column. state = -1 is set by the discover() method of the installer adapters (e.g. ComponentAdapter::discover()) for old core files that are present on disk but not registered in the #__extensions table — typically leftovers from a core extension that wasn't cleanly removed during a previous update, or restored from an old backup. It's cleared to 0 only once the discovered extension is actually installed via prepareDiscoverInstall().

Added state != -1 to the query's per-extension WHERE clause, so these discovered-but-unregistered core extensions are excluded from the refresh attempt entirely instead of being attempted and then reported as failed. This doesn't touch Installer::refreshManifestCache() itself or try to address why a core extension ends up at state=-1 after a migration — that's a separate question about the upgrade/discovery path; this only stops a known no-op from being reported as an error.

Testing Instructions

Tested with a real Joomla installation and complete Joomla core updates (not just code review):

  • Joomla base: 5.4-dev, commit 0ec52b315b
  • PR package: 5.4.9-dev+pr.48446, commit e4daeff3f0
  • PHP 8.3.31
  • MariaDB 11.4.13
  • Fresh Joomla installation created with Joomla's supported CLI installer
  • Frontend and administrator both returned HTTP 200 after the test

Use a disposable Joomla 5.4-dev installation or two identical copies of one installation. Replace j54_ below with your site's database prefix.

  1. In phpMyAdmin or another database tool, confirm the core rows exist:
SELECT extension_id, name, type, element, folder, client_id, state
FROM j54_extensions
WHERE type = 'library'
  AND element IN ('joomla', 'phpass')
ORDER BY extension_id;
  1. Simulate the migration artifact and add a happy-path marker:
UPDATE j54_extensions
SET state = -1,
    manifest_cache = 'DISABLED_SENTINEL'
WHERE type = 'library'
  AND element = 'phpass'
  AND folder = ''
  AND client_id = 0;

UPDATE j54_extensions
SET manifest_cache = 'ACTIVE_SENTINEL'
WHERE type = 'library'
  AND element = 'joomla'
  AND folder = ''
  AND client_id = 0;
  1. Before applying this PR, perform a Joomla core update or reinstall. For a reproducible 5.4-dev control, use Joomla CI's unrelated PR 48450 package, which does not contain this fix:
php cli/joomla.php core:update:channel custom \
  --url='https://artifacts.joomla.org/drone/joomla/joomla-cms/5.4-dev/48450/downloads/96430/pr_list.xml' \
  --no-interaction

php cli/joomla.php core:update:check --no-interaction
php cli/joomla.php core:update --no-interaction --no-ansi

Alternatively, use System → Update → Joomla with that custom update URL.

  1. Before the PR, the update finishes with errors. My exact result was:
[ERROR] Update finished with errors. Please check logs for details.

The update log contained:

Refresh Manifest Cache failed: lib_phpass Extension is not currently installed.
An error has occurred while running "JoomlaInstallerScript::updateManifestCaches". Code: 0. Message: Error on updating manifest cache: (type, element, folder, client) = (library, phpass, lib_phpass, 0).
  1. Restore the original disposable installation/database snapshot, or repeat on the second identical installation. Run the two SQL UPDATE statements again.

  2. Configure Joomla to use this PR's official update package instead, and repeat the update:

php cli/joomla.php core:update:channel custom \
  --url='https://artifacts.joomla.org/drone/joomla/joomla-cms/5.4-dev/48446/downloads/96422/pr_list.xml' \
  --no-interaction

php cli/joomla.php core:update:check --no-interaction
php cli/joomla.php core:update --no-interaction --no-ansi

The same operation can be run from System → Update → Joomla after setting the custom update URL in the Joomla Update options.

  1. Expected result with this PR:
[OK] Joomla core updated successfully!

There should be no updateManifestCaches/lib_phpass error.

  1. Confirm that the discovered-but-unregistered extension was skipped while an active core extension was still refreshed:
SELECT
    extension_id,
    name,
    element,
    state,
    manifest_cache = 'DISABLED_SENTINEL' AS disabled_sentinel_unchanged,
    manifest_cache = 'ACTIVE_SENTINEL' AS active_sentinel_unchanged,
    JSON_VALID(manifest_cache) AS valid_manifest_json,
    JSON_UNQUOTE(JSON_EXTRACT(manifest_cache, '$.version')) AS manifest_version
FROM j54_extensions
WHERE type = 'library'
  AND element IN ('joomla', 'phpass')
ORDER BY extension_id;

My result after applying the PR was:

lib_joomla  state=0   active_sentinel_unchanged=0   valid_manifest_json=1   manifest_version=13.1
lib_phpass  state=-1  disabled_sentinel_unchanged=1 valid_manifest_json=0

This confirms that the state=-1 extension was excluded while the normal core extension still had its manifest cache refreshed.

Actual result BEFORE applying this Pull Request

Any core extension sitting at state = -1 causes updateManifestCaches() to report an "Error on updating manifest cache" during every subsequent Joomla core update or reinstall, even though the extension was correctly and intentionally skipped.

Expected result AFTER applying this Pull Request

Core extensions with state = -1 are excluded from the refresh query entirely, so they no longer produce a spurious error during updates.

Link to documentations

Please select:

  • Documentation link for guide.joomla.org:

  • No documentation changes for guide.joomla.org needed

  • Pull Request link for manual.joomla.org:

  • No documentation changes for manual.joomla.org needed

avatar wakqasahmed wakqasahmed - open - 12 Sep 2026
avatar wakqasahmed wakqasahmed - change - 12 Sep 2026
Status New ⇒ Pending
avatar joomla-cms-bot joomla-cms-bot - change - 12 Sep 2026
Category ⇒ Administration com_admin
avatar richard67
richard67 - comment - 12 Sep 2026

Testing Instructions

No existing test harness covers this script (it's a legacy install/update hook with no unit tests, and a full behavioral test would need a DB-backed core-update simulation).

@wakqasahmed A code review is definitely not a sufficient test. It needs to do a real test with a cure update.

Things which are relevant for end users have to be tested with real human tests.

In addition, we require that contributors have tested their PRs themselves before submitting. Your testing instructions read as if you have not done such a real test and relied on code review (with help of AI). So this requirements seems not to be fulfilled either.

Please provide useful testing instructions for end users for other testers and test your changes with these instructions, too.

Thanks in advance.

avatar wakqasahmed
wakqasahmed - comment - 13 Sep 2026

I have now tested this with a real Joomla installation and complete Joomla core updates, rather than relying on code review.

Environment tested

  • Joomla base: current 5.4-dev, commit 0ec52b315b
  • PR package: 5.4.9-dev+pr.48446, commit e4daeff3f0
  • PHP 8.3.31
  • MariaDB 11.4.13
  • Fresh Joomla installation created with Joomla's supported CLI installer
  • Frontend and administrator both returned HTTP 200 after the test

Testing instructions

Use a disposable Joomla 5.4-dev installation or two identical copies of one installation. Replace j54_ below with your site's database prefix.

  1. In phpMyAdmin or another database tool, confirm the core rows exist:
SELECT extension_id, name, type, element, folder, client_id, state
FROM j54_extensions
WHERE type = 'library'
  AND element IN ('joomla', 'phpass')
ORDER BY extension_id;
  1. Simulate the migration artifact and add a happy-path marker:
UPDATE j54_extensions
SET state = -1,
    manifest_cache = 'DISABLED_SENTINEL'
WHERE type = 'library'
  AND element = 'phpass'
  AND folder = ''
  AND client_id = 0;

UPDATE j54_extensions
SET manifest_cache = 'ACTIVE_SENTINEL'
WHERE type = 'library'
  AND element = 'joomla'
  AND folder = ''
  AND client_id = 0;
  1. Before applying this PR, perform a Joomla core update or reinstall.

For a reproducible 5.4-dev control, I used Joomla CI's unrelated PR 48450 package, which does not contain this fix:

php cli/joomla.php core:update:channel custom \
  --url='https://artifacts.joomla.org/drone/joomla/joomla-cms/5.4-dev/48450/downloads/96430/pr_list.xml' \
  --no-interaction

php cli/joomla.php core:update:check --no-interaction
php cli/joomla.php core:update --no-interaction --no-ansi

Alternatively, use System → Update → Joomla with that custom update URL.

  1. Before the PR, the update finishes with errors. My exact result was:
[ERROR] Update finished with errors. Please check logs for details.

The update log contained:

Refresh Manifest Cache failed: lib_phpass Extension is not currently installed.
An error has occurred while running "JoomlaInstallerScript::updateManifestCaches". Code: 0. Message: Error on updating manifest cache: (type, element, folder, client) = (library, phpass, lib_phpass, 0).
  1. Restore the original disposable installation/database snapshot, or repeat on the second identical installation. Run the two SQL UPDATE statements again.

  2. Configure Joomla to use this PR's official update package:

php cli/joomla.php core:update:channel custom \
  --url='https://artifacts.joomla.org/drone/joomla/joomla-cms/5.4-dev/48446/downloads/96422/pr_list.xml' \
  --no-interaction

php cli/joomla.php core:update:check --no-interaction
php cli/joomla.php core:update --no-interaction --no-ansi

The same operation can be run from System → Update → Joomla after setting the custom update URL in the Joomla Update options.

  1. Expected result with this PR:
[OK] Joomla core updated successfully!

There should be no updateManifestCaches/lib_phpass error.

  1. Confirm that the disabled extension was skipped while an active core extension was still refreshed:
SELECT
    extension_id,
    name,
    element,
    state,
    manifest_cache = 'DISABLED_SENTINEL' AS disabled_sentinel_unchanged,
    manifest_cache = 'ACTIVE_SENTINEL' AS active_sentinel_unchanged,
    JSON_VALID(manifest_cache) AS valid_manifest_json,
    JSON_UNQUOTE(JSON_EXTRACT(manifest_cache, '$.version')) AS manifest_version
FROM j54_extensions
WHERE type = 'library'
  AND element IN ('joomla', 'phpass')
ORDER BY extension_id;

My result after applying the PR was:

lib_joomla  state=0   active_sentinel_unchanged=0   valid_manifest_json=1   manifest_version=13.1
lib_phpass  state=-1  disabled_sentinel_unchanged=1 valid_manifest_json=0

This confirms that the state=-1 extension was excluded while the normal core extension still had its manifest cache refreshed.

avatar wakqasahmed wakqasahmed - change - 13 Sep 2026
Labels Added: PR-5.4-dev
avatar brianteeman
brianteeman - comment - 13 Sep 2026

Surely the correct approach is to determine why/how those extensions were able to get a state of -1 which should not be possible

avatar richard67
richard67 - comment - 13 Sep 2026

I have now tested this with a real Joomla installation and complete Joomla core updates, rather than relying on code review.

Testing instructions

@wakqasahmed I've asked you to update the testing instructions in the PR description (initial post) so other testers can find them.

avatar richard67
richard67 - comment - 13 Sep 2026

Surely the correct approach is to determine why/how those extensions were able to get a state of -1 which should not be possible

A state of -1 is set for extensions in the discover methods of the diverse installer adapters, e.g. for components here: https://github.com/joomla/joomla-cms/blob/5.4-dev/libraries/src/Installer/Adapter/ComponentAdapter.php#L1279

Later, when installing the discovered extensions, the state is set to zero before trying to store the database record in the extensions table in methods https://github.com/joomla/joomla-cms/blob/5.4-dev/libraries/src/Installer/Adapter/ComponentAdapter.php#L1279 of the installer adapters, e.g. for components here: https://github.com/joomla/joomla-cms/blob/5.4-dev/libraries/src/Installer/Adapter/ComponentAdapter.php#L639

So a state of -1 happens for old core extensions which were not properly uninstalled during a previous core update, or they have been properly uninstalled but later an old backup was restored where the files still were present, so the extension discovery has found them.

Now the question for this PR here is do we want to skip these extension when doing the update, like suggested by this PR?

avatar richard67
richard67 - comment - 13 Sep 2026

Surely the correct approach is to determine why/how those extensions were able to get a state of -1 which should not be possible

A state of -1 is set for extensions in the discover methods of the diverse installer adapters, e.g. for components here: https://github.com/joomla/joomla-cms/blob/5.4-dev/libraries/src/Installer/Adapter/ComponentAdapter.php#L1279

Later, when installing the discovered extensions, the state is set to zero before trying to store the database record in the extensions table in methods prepareDiscoverInstall of the installer adapters, e.g. for components here: https://github.com/joomla/joomla-cms/blob/5.4-dev/libraries/src/Installer/Adapter/ComponentAdapter.php#L639

So a state of -1 happens for old core extensions which were not properly uninstalled during a previous core update, or they have been properly uninstalled but later an old backup was restored where the files still were present, so the extension discovery has found them.

Now the question for this PR here is do we want to skip these extension when doing the update, like suggested by this PR?

avatar wakqasahmed
wakqasahmed - comment - 14 Sep 2026

Thanks for digging into the discover/prepareDiscoverInstall path — that matches what I saw for phpass/plg_quickicon_eos after the migration. My take: yes, skipping them in the refresh query seems like the safer near-term fix regardless of how they got to state=-1 in the first place, since refreshManifestCache() already treats that state as an intentional no-op elsewhere. Happy to adjust the approach if the team decides the real fix belongs in the discovery/migration path instead.

avatar richard67
richard67 - comment - 14 Sep 2026

@wakqasahmed So or so the title "Skip disabled core extensions ..." of your PR is wrong, the extensions are not disabled, they are discovered. The disabled states is in another database column (enabled).

And you still haven't added useful testing instructions for human testers to the description (initial post) of your PR so testers can find them. They will not find them buried in the comment thread of this PR.

I have asked before 2 times to do that.

avatar wakqasahmed wakqasahmed - change - 19 Sep 2026
Title
…
[AI] Skip disabled core extensions in updateManifestCaches() (#48034)
[AI] Skip discovered core extensions in updateManifestCaches() (#48034)
avatar wakqasahmed wakqasahmed - edited - 19 Sep 2026
avatar wakqasahmed wakqasahmed - change - 19 Sep 2026
The description was changed
avatar wakqasahmed wakqasahmed - edited - 19 Sep 2026
avatar wakqasahmed
wakqasahmed - comment - 19 Sep 2026

@richard67 Both addressed:

  1. Title: Fixed. Old title said "Skip disabled core extensions ..."; new title is "[AI] Skip discovered core extensions in updateManifestCaches() (#48034)". You're right that state = -1 isn't the enabled column — I confirmed it's set by the installer adapters' discover() methods (e.g. ComponentAdapter::discover(), line 1297) for old core files present on disk but not registered in #__extensions, and only cleared once the discovered extension is actually installed via prepareDiscoverInstall(). Updated the PR description to explain this distinction as well.

  2. Testing instructions: Moved into the PR description (initial post) under "Testing Instructions", including the environment details, the SQL setup/verification steps, and the before/after core-update commands and results from my comment above. They're no longer only in the comment thread.

Thanks for the pointers on the discover/prepareDiscoverInstall path — that context is now reflected in the Summary of Changes too.

avatar wakqasahmed
wakqasahmed - comment - 24 Sep 2026

Hi @richard67 @HLeithner — noticed this PR doesn't have a reviewer assigned yet — it's been sitting for a while, CI is green and it's mergeable. Would you (or whoever's best placed) be able to take a look, or point me to who should? Thanks!

avatar muhme
muhme - comment - 25 Sep 2026

@wakqasahmed Thank you for your contribution!

The Joomla bug-fixing process requires two successful human tests by people other than the contributor, documented in the issue tracker, before we can consider merging the PR.

A code review is independent of that process, and we usually don't assign reviewers to pull requests.

As we are all volunteers, getting enough testers can sometimes take a while. Helping hands are always welcome — perhaps you could also help by testing some other pull requests 🙏 That would be greatly appreciated!

avatar wakqasahmed
wakqasahmed - comment - 1 Oct 2026

Thanks for clarifying the process, @muhme — makes sense that review and the two-tester sign-off are separate tracks. I'll keep an eye on the issue tracker for #48034 and pitch in on testing other PRs in the meantime.

avatar wakqasahmed
wakqasahmed - comment - 5 Oct 2026

Hi @richard67 @muhme — circling back on this one — still no reviewer assigned, quiet for a while since the last activity. No pressure, just don't want it to get lost.

avatar richard67
richard67 - comment - 5 Oct 2026

Hi @richard67 @muhme — circling back on this one — still no reviewer assigned, quiet for a while since the last activity. No pressure, just don't want it to get lost.

@wakqasahmed Every PR needs 2 successful tests by humans, and this has to be carried out by volunteers. Of course if all volunteers propose PRs but (almost) nobody of them tests other people's PRs, then we have a backlog.

Keeping maintainers and release managers busy with such "reminders" doesn't help the project either.

avatar HLeithner
HLeithner - comment - 5 Nov 2026

This pull request has been automatically rebased to 6.2-dev. Joomla! 5.4 is now in security only mode.

avatar richard67 richard67 - change - 6 Nov 2026
Title
…
[AI] Skip discovered core extensions in updateManifestCaches() (#48034)
[6.2] [AI] Skip discovered core extensions in updateManifestCaches() (#48034)
avatar richard67 richard67 - edited - 6 Nov 2026

Add a Comment

Login with GitHub to post a comment