UpdateChecker permanently breaks the Overview panel after upgrade

Nextcloud version: 16.0.5
Operating system: ubuntu server 16.04 LTS
Apache version: 2.4.18
PHP version: 7.1

Hi,

my internal nextcloud server was still running on version 12.0.6 so it was high time to update.

The VM has no direct internet access, thus I put in appropriate proxy settings. This did not get rid of the warning message on the overview page, so I set out to perform the upgrades manually.

To bring it up to date, I’ve first updated to apache2.4.18 and php7.1 (no immediate issues there). Then I performed the following server upgrades in sequence:

13.0.12 -> 14.0.14 -> 15.0.12 -> 16.0.5

I have tested access to the system and created backups of the server directory and DB for every step.

Since the upgrade to 14.0.14 I am now facing the following problem in every version:

  • Immediately after the upgrade, the overview page is accessible.
    It shows the correct version, along with:
The update check is not yet finished. Please refresh the page. 
Update channel: Stable  

Both the GUI log and server logfile list the same key issue:

"Message":"strpos() expects parameter 1 to be string, null given","Code":0,"Trace":[{"file":"/opt/nextcloud/apps/updatenotification/lib/UpdateChecker.php","line":64,"function":"strpos","args":[null,"https://"]},
Expand full log entry

{“reqId”:“wo2SFucW8tcCycqAuY2E”,“level”:3,“time”:“2019-11-08T19:39:01+00:00”,“remoteAddr”:“10.16.4.109”,“user”:“LDAP-ID-censored”,“app”:“index”,“method”:“GET”,“url”:“/settings/admin/overview”,“message”:{“Exception”:“TypeError”,“Message”:“strpos() expects parameter 1 to be string, null given”,“Code”:0,“Trace”:[{“file”:“/opt/nextcloud/apps/updatenotification/lib/UpdateChecker.php”,“line”:64,“function”:“strpos”,“args”:[null,“https://”]},
{“file”:“/opt/nextcloud/apps/updatenotification/lib/Settings/Admin.php”,“line”:83,“function”:“getUpdateState”,“class”:“OCA\UpdateNotification\UpdateChecker”,“type”:“->”,“args”:},
{“file”:“/opt/nextcloud/settings/Controller/CommonSettingsTrait.php”,“line”:115,“function”:“getForm”,“class”:“OCA\UpdateNotification\Settings\Admin”,“type”:“->”,“args”:},
{“file”:“/opt/nextcloud/settings/Controller/AdminSettingsController.php”,“line”:74,“function”:“formatSettings”,“class”:“OC\Settings\Controller\AdminSettingsController”,“type”:“->”,“args”:[{“10”:[{“class”:“OC\Settings\Admin\Overview”}],“11”:[{“class”:“OCA\UpdateNotification\Settings\Admin”}]}]},
{“file”:“/opt/nextcloud/settings/Controller/CommonSettingsTrait.php”,“line”:126,“function”:“getSettings”,“class”:“OC\Settings\Controller\AdminSettingsController”,“type”:“->”,“args”:[“overview”]},
{“file”:“/opt/nextcloud/settings/Controller/AdminSettingsController.php”,“line”:65,“function”:“getIndexResponse”,“class”:“OC\Settings\Controller\AdminSettingsController”,“type”:“->”,“args”:[“admin”,“overview”]},
{“file”:“/opt/nextcloud/lib/private/AppFramework/Http/Dispatcher.php”,“line”:166,“function”:“index”,“class”:“OC\Settings\Controller\AdminSettingsController”,“type”:“->”,“args”:[“overview”]},
{“file”:“/opt/nextcloud/lib/private/AppFramework/Http/Dispatcher.php”,“line”:99,“function”:“executeController”,“class”:“OC\AppFramework\Http\Dispatcher”,“type”:“->”,“args”:[{“class”:“OC\Settings\Controller\AdminSettingsController”},“index”]},
{“file”:“/opt/nextcloud/lib/private/AppFramework/App.php”,“line”:126,“function”:“dispatch”,“class”:“OC\AppFramework\Http\Dispatcher”,“type”:“->”,“args”:[{“class”:“OC\Settings\Controller\AdminSettingsController”},“index”]},
{“file”:“/opt/nextcloud/lib/private/AppFramework/Routing/RouteActionHandler.php”,“line”:47,“function”:“main”,“class”:“OC\AppFramework\App”,“type”:“::”,“args”:[“OC\Settings\Controller\AdminSettingsController”,“index”,{“class”:“OC\AppFramework\DependencyInjection\DIContainer”},
{“section”:“overview”,“_route”:“settings.AdminSettings.index”}]},
{“function”:“__invoke”,“class”:“OC\AppFramework\Routing\RouteActionHandler”,“type”:“->”,“args”:[{“section”:“overview”,“_route”:“settings.AdminSettings.index”}]},
{“file”:“/opt/nextcloud/lib/private/Route/Router.php”,“line”:297,“function”:“call_user_func”,“args”:[{“class”:“OC\AppFramework\Routing\RouteActionHandler”},
{“section”:“overview”,“_route”:“settings.AdminSettings.index”}]},
{“file”:“/opt/nextcloud/lib/base.php”,“line”:975,“function”:“match”,“class”:“OC\Route\Router”,“type”:“->”,“args”:[“/settings/admin/overview”]},
{“file”:“/opt/nextcloud/index.php”,“line”:42,“function”:“handleRequest”,“class”:“OC”,“type”:“::”,“args”:}],“File”:“/opt/nextcloud/apps/updatenotification/lib/UpdateChecker.php”,“Line”:64,“CustomMessage”:“–”},
“userAgent”:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36”,“version”:“16.0.5.1”}

The relevant code snippet, line 64 of nextcloud/apps/updatenotification/lib/UpdateChecker.php :

if (strpos($data['changes'], 'https://') === 0) { ...

So for some reason, $data['changes'] somehow ends up being null the moment it’s called in the script, but only after the update check mentioned earlier has “completed”.

$data is populated by $data = $this->updater->check(); in line 49 of the same file.

Expand full code section
namespace OCA\UpdateNotification;

use OC\Updater\ChangesCheck;
use OC\Updater\VersionCheck;

class UpdateChecker {
        /** @var VersionCheck */
        private $updater;
        /** @var ChangesCheck */
        private $changesCheck;

        /**
         * @param VersionCheck $updater
         */
        public function __construct(VersionCheck $updater, ChangesCheck $changesCheck) {
                $this->updater = $updater;
                $this->changesCheck = $changesCheck;
        }

        /**
         * @return array
         */
        public function getUpdateState(): array {
                $data = $this->updater->check();
                $result = [];

                if (isset($data['version']) && $data['version'] !== '' && $data['version'] !== []) {
                        $result['updateAvailable'] = true;
                        $result['updateVersion'] = $data['version'];
                        $result['updateVersionString'] = $data['versionstring'];
                        $result['updaterEnabled'] = $data['autoupdater'] === '1';
                        $result['versionIsEol'] = $data['eol'] === '1';
                        if (strpos($data['web'], 'https://') === 0) {
                                $result['updateLink'] = $data['web'];
                        }
                        if (strpos($data['url'], 'https://') === 0) {
                                $result['downloadLink'] = $data['url'];
                        }
                        if (strpos($data['changes'], 'https://') === 0) {

Sadly that’s about where my total lack of PHP knowledge fails me.

I have tried reapplying file ownership throughout the server directory.

I’ve researched the issue online obviously. There are many instances of wrong php parameters, but only one citing this exact issue and function, also matching my experience with the version 14 upgrade, but with a disappointing resolution:

In any case the issue does not seem to be “going away by itself” for me.

Any support in finding and fixing this problem would be greatly appreciated.

if (isset($data['changes']) && strpos($data['changes'], 'https://') === 0) {

Something like above perhaps? It’s a bit confusing that changes is empty but if strpos does not like null we need to check it before.

Thanks for the reply!

Commenting out line 64 and trying your suggestion, it seems to run fine - status page works, and no new errors in the log.

I am not sure if it’s okay that the function simply doesn’t run if changes is null - but it might be as it’s related to the updater.

It probably it only ends up being null if there are no updates found, which is why the other guy said it fixed itself after a while? But if it were just that, I can’t believe I’m just the second person to run into that issue.

I looked at the changelog for strpos() for php7.1, thinking it could be related to the php update, but couldn’t find any change in that regard. Maybe there was a change in how the check() function behaves in that it was never quite empty up until version 14?

I got everything else straightened out, especially the internet access aspect.
The updater seems to work perfectly fine now:

Screenshot

Should I raise a bug report?
It does seem to affect current systems that were upgraded from pre-v14 editions, so I think it’s relevant.

It probably it only ends up being null if there are no updates found, which is why the other guy said it fixed itself after a while? But if it were just that, I can’t believe I’m just the second person to run into that issue.

I found it :wink:

We don’t add the changes element for nextcloud versions < 14.

But we cache the response from the updater_server for 30 minutes.

If you update from 12.x to 13.x everything is alright. But after 13.x to 14.x the updater code cannot parse the cached response. It will solve itself after 30 minutes :wink:

So the above patch just prevents the access to the non existing element. The changes information is not required for the update itself. The right way to fix this is probably something like a repair / upgrade job which clears the updater cache.

cc @nickvergessen @blizzz

That makes sense. I’ll revert the code and monitor it, but expect the issue to remain solved.

Thank you for taking the time.