Skip to content

v2.35.0 — the update check could not report an update

Released 2026-08-27. Upgrade:

sudo snap refresh yazses     # snap
pipx upgrade yazses          # or: uv tool upgrade yazses
scoop update yazses          # Windows

Two fixes in one function's blast radius, both reported or found by running the shipped product rather than by any test. Between them, neither bundled platform could tell you that a new version existed — Windows said nothing at all, and macOS said something confidently wrong.

Windows: "Check for updates…" never showed an update

Reported from a Windows desktop: the tray's Check for updates… never announced a new version and never updated anything, so upgrading meant uninstalling and installing the new build by hand.

Windows gives a balloon body a 256-wide-character buffer (NOTIFYICONDATA.szInfo) and discards an oversized balloon whole — no truncation, no exception, nothing in a log. Measured against the real messages:

Case Body length Result
Windows installer, update available 512 dropped
check failed / offline 623 dropped
up to date 40 shown
Scoop / Chocolatey / winget, update available 60–79 shown

The two cases that carry information were exactly the two that vanished. The only message able to render was "YazSes is up to date", which is why the entry looked like it could never find anything.

This had already been found and fixed for About, six lines above in the same file, and the update path was not covered. The fitting now happens inside the one function that reaches the Windows notification API, so every balloon — including the daemon's relayed self-healing messages, which are the longest the tray shows — inherits it, and a structural test fails the build if any call bypasses it. A test pinning one path would have passed on the other bug, which is how this shipped twice.

And the click now does something. A Windows-installer install has no upgrade command by design — the upgrade is a downloaded .exe — so the tray's whole response was to print the releases URL into a balloon, where it is text rather than a link. It now opens the download page, and says the installer upgrades in place and keeps your settings and models, which is what makes the uninstall-first workaround unnecessary.

Downloading and running the installer automatically is deliberately not done: that is executing a fetched binary on your behalf, and it waits on code signing.

macOS: the .app was told to download a Windows .exe

Found next to the balloon bug, in the same function. detect_install_method classified anything with sys.frozen set as windows-installer, because the frozen branch was written when Windows was the only bundled build. The .dmg .app is frozen too, so every Mac user who opened Check for updates… was told, in confident step-by-step detail, to download a YazSes-<version>-windows-<arch>.exe — a file that cannot run on their machine, in a message with nothing to suggest the advice itself was the problem.

A wrong instruction stated with certainty is worse than a silent failure: the reader has no reason to doubt it.

A bundle is not a Windows bundle; it is a bundle on whichever OS is running it. There is now a macos-app method with:

  • its own steps — open the current .dmg, drag YazSes to Applications, replacing the old copy;
  • its own recovery hint on the update did nothing page, which gained the macOS tab it never had;
  • Homebrew named as the alternative (brew upgrade --cask yazses) rather than guessed at — a cask install and a direct .dmg install are the same bundle and are not distinguishable from inside it, and a command that exits 0 having done nothing is the failure this whole module keeps being rewritten against;
  • the GitHub release as its version source, because the artifact is a release asset and PyPI carries no .dmg.

The macOS tray's Check for updates… now opens the download page for the same reason the Windows one does, and yazses update --check on a .app stops exiting 1 with "no automatic upgrade is available" — the method is recognised, so it prints real steps and exits 0.

Also

The balloon title is now bounded too. NOTIFYICONDATA.szInfoTitle is a 64-wide-character buffer, the same family of fixed buffer as the body. No title YazSes ships comes close — the longest is 47 characters — so this is the class being closed rather than a live overrun, and the same structural test now covers both fields of every notify() call in the Windows tray.

Nothing else changed

Dictation, the models, your config and the learning corpus are untouched. Everything above is in the update path and the tray.