· Infra · 3 min read
Mattermost Desktop shows a generic cog in the GNOME dock on Wayland
A follow-up to the earlier Mattermost install post. Sandbox SUID fixed, app launches, app works — but the dock icon was wrong. The launcher in the app grid showed the Mattermost logo. The moment I clicked it, the running app got pinned to the dock with a generic gear/cog icon. Quitting and relaunching changed nothing.
Took me a few false starts to figure out, so writing it up. The root cause generalizes to any Electron app where the dock won't pick up the icon on Wayland.
What I tried first (and why it didn't work)
The standard advice is "the .desktop filename should match the window's WM_CLASS, or set StartupWMClass in the .desktop file." So I tried:
~/.local/share/applications/mattermost-desktop.desktop
with StartupWMClass=Mattermost. Nope.
Renamed it to match upstream's bundled create_desktop_file.sh:
~/.local/share/applications/Mattermost.desktop
Still a generic cog.
The catch is that on Wayland there is no WM_CLASS — that's an X11 property. xprop and wmctrl both return nothing for native Wayland clients. GNOME's org.gnome.Shell.Introspect.GetWindows returns AccessDenied on a standard session. So the usual reflex of "just run xprop and read the class off the window" doesn't apply, and that's the whole problem: it's hard to discover what the window is actually broadcasting.
What Wayland actually matches on
On Wayland, the toplevel surface sets its identity via xdg_toplevel.set_app_id("..."). GNOME's dock looks up that string against .desktop files in the standard search paths — basename match first (<app_id>.desktop), then a StartupWMClass field match. If neither hits, you get the generic icon.
So the question reduces to: what app_id is the Electron binary actually setting?
Electron's default on Linux is app.getName(), which returns productName from package.json if set, otherwise name. Inside Mattermost's app.asar:
"name": "mattermost-desktop"
"productName": "Mattermost"
setAppUserModelId("Mattermost.Desktop")
Three candidates. setAppUserModelId is documented as a Windows-only API for taskbar grouping, so I dismissed it. Turned out to be the right answer.
The diagnostic that worked: journalctl
When gdbus is locked down and there's no xprop, the journal is the surprisingly direct way in. Every app launched via gnome-shell's launcher gets wrapped in a transient systemd scope, and the scope name embeds the app_id:
journalctl --user -b --since "5 minutes ago" | grep -i mattermost
... Started app-gnome-Mattermost-46369.scope - Application launched by gnome-shell.
... Started app-Mattermost.Desktop-46369.scope.
Two scopes. The first (app-gnome-Mattermost-…) is gnome-shell launching the .desktop file I gave it — that part's working. The second one is what Electron registers itself as via systemd-run/org.freedesktop.systemd1.Manager.StartTransientUnit — and that's the scope name app-<app_id>-<pid>.scope. So the app_id is Mattermost.Desktop, with the dot.
That's setAppUserModelId taking effect on Linux. Whether that's intentional or accidental on Electron's part I don't know, but it's the value being passed to xdg_toplevel.set_app_id, and it's what the dock is looking for.
The fix
Rename the .desktop file so its basename matches the app_id exactly, dot and all:
mv ~/.local/share/applications/Mattermost.desktop \
~/.local/share/applications/Mattermost.Desktop.desktop
update-desktop-database ~/.local/share/applications
Fully quit Mattermost (the matching is sticky to first window mapping, so a relaunch is required) and start it again. Dock icon picks up the proper Mattermost logo immediately.
The .deb from GitHub releases ships a mattermost-desktop.desktop plus a hook that handles this correctly — yet another reason the hidden .deb is the better install path. If you're already on the tarball, the one-line rename above is the fix.
Why this is worth flagging
The standard "fix dock icons on Linux" advice — WM_CLASS, StartupWMClass, xprop — is X11-era and quietly stops working under Wayland. None of the X11 tools tell you the answer, GNOME's introspection is locked behind a permission you don't have, and the actual app_id can be set to a value that doesn't appear in any user-visible string anywhere (it's a Windows-API call in the source code).
If you hit a generic-cog icon for an Electron app on Wayland, the flow is:
journalctl --user -b --since "5 minutes ago" | grep -i <app>— find theapp-<app_id>-<pid>.scopeline. That string betweenapp-and-<pid>is the actual app_id.- Name your
.desktopfile<app_id>.desktopexactly — preserve case, dots, and any other special characters. update-desktop-databaseand relaunch the app from scratch.
That's the whole recipe. It would be nice if Electron documented which package.json fields drive the Linux app_id on which platform (name? productName? setAppUserModelId?), but until then, the systemd scope name in the journal is the source of truth.