Bug 524077 - SnapBackend never signals update-check completion with zero upgradeable snaps, leaving Updates page stuck forever
Summary: SnapBackend never signals update-check completion with zero upgradeable snaps...
Status: RESOLVED FIXED
Alias: None
Product: Discover
Classification: Applications
Component: Snap Backend (other bugs)
Version First Reported In: 6.7.4
Platform: Other Linux
: VHI major
Target Milestone: ---
Assignee: Plasma Bugs List
URL:
Keywords: regression
: 500513 (view as bug list)
Depends on:
Blocks:
 
Reported: 2026-08-09 21:33 UTC by Dolphin Whisperer
Modified: 2026-08-26 21:04 UTC (History)
4 users (show)

See Also:
Latest Commit:
Version Fixed/Implemented In: 6.7.5 and 6.8.0
Sentry Crash Report:


Attachments
Fix: emit contentsChanged() after update check, plus 15s timeout safety net (2.53 KB, patch)
2026-08-09 21:33 UTC, Dolphin Whisperer
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Dolphin Whisperer 2026-08-09 21:33:21 UTC
## Summary

`SnapBackend::checkForUpdates()` never signals completion of its update check when there are zero upgradeable snaps — which is the common, everyday case. This leaves Discover's Updates page permanently stuck on "Fetching updates" for any user with the Snap backend enabled, independent of bug 521682 (see "See Also" — that bug's regression is on `master`'s `EmitWhenChanged` refactor and doesn't exist in the 6.7.x stable branch; this is a different, older mechanism that predates that refactor).

## Root cause

`libdiscover/backends/SnapBackend/SnapBackend.cpp`, `checkForUpdates()`:

```cpp
void SnapBackend::checkForUpdates()
{
    if (m_updatesFetcher) {
        qWarning() << "Already fetching updates";
        return;
    }

    m_updatesFetcher = new StoredResultsStream({populate(m_client.findRefreshable())});
    connect(m_updatesFetcher, &StoredResultsStream::finishedResources, this, [this](const QVector<StreamResult> &resources) {
        for (SnapResource *res : std::as_const(m_resources)) {
            bool contained = kContains(resources, [res](const StreamResult &in) {
                return in.resource == res;
            });
            if (contained) {
                res->setState(AbstractResource::Upgradeable);
                res->updateSizes();
            }
        }
    });
}
```

If `resources` (from `m_client.findRefreshable()`) is empty, the loop never calls `res->setState(AbstractResource::Upgradeable)` for anything. That state-change call is the *only* thing that would trigger `StandardBackendUpdater::resourcesChanged()`, which starts the timer that eventually calls `refreshUpdateable()`. `SnapBackend` also never emits `contentsChanged`/`invalidated` on its own after this callback.

Since `refreshUpdateable()` is the only place that calls `setSettingUp(false)`, and `setSettingUp(false)` is the only place `m_hasBeenPopulated` gets set `true`, when there are zero upgradeable snaps `refreshUpdateable()` for the Snap updater is **never called even once**. `m_hasBeenPopulated` stays `false` for the life of the process, so `isFetchingUpdates()` (`fetchingUpdatesProgress() != 100 || m_settingUp || !m_hasBeenPopulated`) is permanently `true`, and since `ResourcesUpdatesModel::isFetching()` is `true` if *any* backend updater reports fetching, the whole Updates page hangs forever — even though every other backend (PackageKit, Flatpak, KNS, fwupd) completes normally.

Confirmed via a live diagnostic build (`ResourcesUpdatesModel::refreshFetching()` instrumented to log every updater's state every 3s): every other backend transitions to `isFetchingUpdates=false` within seconds; `SnapBackend` alone stays `true` indefinitely, with `isProgressing=false` and `fetchingUpdatesProgress=100` — the exact signature of `!m_hasBeenPopulated` never clearing.

`snap refresh --list` on the affected machine reports "All snaps up to date" — i.e. this reproduces precisely because there's nothing to update, which is presumably the *normal* steady state for most installations.

## Fix

```diff
--- a/libdiscover/backends/SnapBackend/SnapBackend.cpp
+++ b/libdiscover/backends/SnapBackend/SnapBackend.cpp
@@ checkForUpdates()
             if (contained) {
                 res->setState(AbstractResource::Upgradeable);
                 res->updateSizes();
             }
         }
+        // If nothing is upgradeable, no resource's state changes, so nothing
+        // downstream ever notices this check completed: StandardBackendUpdater
+        // only reacts to a resource transitioning to/from Upgradeable, or to
+        // contentsChanged/invalidated. Without an explicit signal here,
+        // refreshUpdateable() is never called even once when there are no
+        // pending snap updates, so m_hasBeenPopulated never becomes true and
+        // Discover's Updates page is stuck on "Fetching updates" forever.
+        Q_EMIT contentsChanged();
     });
 }
```

Also added a defensive 15s timeout around the `QSnapdRequest::runSync()` call in `populateJobsWithFilter()` (unrelated to the above, but `m_threadPool` has only one thread, so if `runSync()` ever genuinely hangs — observed but not fully root-caused — it would permanently block every subsequent snap search too; the timeout gives up and lets the rest of Discover proceed rather than wedging forever).

## Verification

Built locally as a patched RPM (Fedora 44, `plasma-discover-6.7.4-9.fc44.local`) and confirmed live: unpatched build stuck indefinitely on "Fetching updates" with zero snap updates pending; patched build correctly clears and shows "Up to date" within seconds.

## See Also

Bug 521682 — a different bug with an overlapping symptom (both leave Discover stuck on "Fetching updates"), but a distinct mechanism specific to an unreleased `master` refactor. Given how many duplicate reports of 521682 mention Snap specifically, it's plausible some of those reporters are actually hitting *this* bug rather than 521682's regression — worth checking whether their Snap backend also has zero pending updates.

Full diff and this write-up: happy to attach the patch here too if useful, same as I did on 521682.
Comment 1 Dolphin Whisperer 2026-08-09 21:33:35 UTC
Created attachment 195021 [details]
Fix: emit contentsChanged() after update check, plus 15s timeout safety net
Comment 2 Bug Janitor Service 2026-08-22 01:50:48 UTC
A possibly relevant merge request was started @ https://invent.kde.org/plasma/discover/-/merge_requests/1400
Comment 3 Bug Janitor Service 2026-08-25 16:37:38 UTC
A possibly relevant merge request was started @ https://invent.kde.org/plasma/discover/-/merge_requests/1403
Comment 4 Nate Graham 2026-08-25 16:55:50 UTC
Git commit 3a6080aecb513d2694f8b252c83d2711041f5a31 by Nate Graham.
Committed on 25/08/2026 at 16:36.
Pushed by ngraham into branch 'Plasma/6.7'.

SnapBackend: signal completion of update check with zero upgradeable snaps

checkForUpdates() only marked resources contained in the refreshable
list as Upgradeable. When no snaps were upgradeable, nothing triggered
StandardBackendUpdater::resourcesChanged(), so refreshUpdateable() never
ran even once, m_hasBeenPopulated stayed false forever and the Updates
page remained stuck on "Fetching updates".

Emit contentsChanged() after processing the refreshable list so the
updater settles regardless of how many snaps are upgradeable.


(cherry picked from commit 6ec5808f937167103ccbe47a309fd38e263961c8)

Co-authored-by: Dustin Trumps <trumpsdustin@outlook.com>

M  +1    -0    libdiscover/backends/SnapBackend/SnapBackend.cpp

https://invent.kde.org/plasma/discover/-/commit/3a6080aecb513d2694f8b252c83d2711041f5a31
Comment 5 Nate Graham 2026-08-26 04:11:14 UTC
*** Bug 500513 has been marked as a duplicate of this bug. ***