· via dev.to (home feed)
Distrusting a green CI build exposed three Tauri updater bugs, including wrong-language installs
A developer who refused to trust a passing CI build found three Tauri bugs: a macOS universal bundling failure, release uploads skipped without warning, and an updater manifest that serves the wrong installer language.

A developer who refused to take a green CI build at face value has documented three separate defects in Tauri's desktop build and auto-update pipeline. According to a post on dev.to, the findings came out of StackerPulse, a small Rust and Tauri v2 client for Stacker News, and include a broken macOS bundling step, a release action that reports success while uploading nothing, and an updater manifest that can quietly deliver the wrong installer to users.
A universal build that cannot bundle
StackerPulse produces two binaries from a single crate: the application itself and a small CLI secret scanner. Windows and Linux builds pass, but the macOS universal build fails at the bundling step with an error stating that the second binary is missing from the universal output directory.
The author's breakdown shows each component behaving correctly in isolation. The universal build path merges only the main binary with lipo, the function that enumerates binaries properly returns every [[bin]] entry whose required features are enabled, and the bundler dutifully copies everything on that list. The defect appears only where one component's assumptions about another's output break down: the second binary is compiled for both architectures, never merged into the universal directory, and then requested there by name. The author points out that this class of bug can sit hidden in repositories that never build a macOS universal target — he caught it only because one workflow ships all three platforms.
A green release with no installers
After fixing the workflow, the next run also went green. The draft release held CLI archives, checksums and provenance attestations — and no installer at all. According to the post, tauri-action skips uploading when the release ID it receives is empty, then exits with code 0 and raises no warning. The trigger was two lines of workflow configuration that supplied a tag name the action could not resolve into a release. The author considers this one less a Tauri defect than an absent safety check on a step that fails quietly.
A manifest that serves the wrong language
The third bug emerged from comparing the updater manifest, latest.json, against the files actually attached to the release. The manifest's platforms object carries one key per target — windows-x86_64, windows-x86_64-msi, windows-x86_64-nsis — while the project's WiX configuration builds two MSI installers, one for en-US and one for ru-RU.
One key per architecture means only one MSI survives: the Russian build overwrites the English one, and the plain windows-x86_64 entry keeps pointing at English, leaving two entries describing different languages for the same architecture. Every installer is present and signed, every asset check passes, and a Russian user on an MSI install receives an English application after updating — a wrong result that user would likely never report.
The fix and how it was proven
A patch for the first bug already existed when the author investigated, but reviewing it exposed a further gap: Cargo receives both the configuration and command-line feature lists, while get_binaries() filters on only one, so a binary enabled through build.features in tauri.conf.json would still be missed. Pull request tauri#16488 on the Tauri repository merges every binary from get_binaries() with the configuration features folded in first, so the feature filter and the bundler work from an identical list. The author also describes a near-miss of his own: a plan to rename the main binary inside the lipo loop was dropped after reading rename_app(), which already performs the rename afterwards and would have left his change looking for a file that no longer existed.
Without a Mac for local testing, the verification ran in CI on a real runner against two refs of tauri-cli: the dev branch fails with exit 1, while the pull request branch builds with the second binary present inside the app bundle. A build that fails without the patch and succeeds with it is what demonstrates the fix, the author argues; a passing build on its own demonstrates nothing.
Checking artifacts instead of exit codes
The post closes with roughly seven lines of shell that ignore build status entirely: diff the release's asset names against an expected list, use jq to confirm every platform entry in latest.json carries both a URL and a signature, and verify the manifest version matches the release tag. The version in the author's repository additionally checks that download links are pinned to the tag, since draft releases generate untagged URLs that stop working the moment the release is published.
Why it matters
Desktop auto-updaters are a supply-chain surface, and the updater manifest is the contract between a release and every installed client. The third bug is the archetype of what makes that dangerous: nothing fails, every signature validates, and users still receive the wrong artifact without knowing it. A wrong language is an inconvenience; the same silent handover delivering a wrong or stale binary would be far worse. The cheap defence is a handful of jq and gh checks that verify what was actually published rather than what the pipeline claims. A passing build shows only that the toolchain ran to completion — it says nothing about whether the shipped artifact is correct, complete, or even the one you intended to release.
- #tauri
- #rust
- #ci-cd
- #supply-chain
- #auto-updates