Skip to content

Build1 publisherNot yet confirmed elsewhere3 min readPublished

PyInstaller exits zero, then the real work starts: notarization traps that report success

One engineer's log of shipping a Python app to macOS and Windows beta users. Three checkpoints in the signing pipeline hand back a pass on builds that users cannot open.

The Engineer · Build desk

How we use AISend a correction

What happened

  • An engineer spent months building a Python desktop app, then weeks getting it to install and run on other people's macOS and Windows machines.
  • Qt and Python framework binaries carry no file extension, so name-based signing loops skipped them and Apple rejected the notarization 15 minutes later.
  • The notarization tool's clean exit code meant only that the upload finished, so a build Apple marked Invalid was stapled and reported as shipped.
  • Beta testers launching from Downloads or a DMG hit macOS path randomization, which put the app's updater into an endless download-and-relaunch loop.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint A passing verify step cannot be used as release evidence on macOS, because it answers a question about integrity that nobody asked instead of the question about the signer that gates distribution.
  • cost Teams that leave signing checks to Apple's queue pay for every mistake in 15-minute round trips rather than 20-second ones, and the bill lands on whoever is holding the release.
  • exposure Defects that only appear from quarantined download paths are invisible to the people who wrote them, so the discovery route runs through a beta user's broken updater.
  • decision Anyone scheduling a desktop release has to decide where weeks of unbudgeted distribution work sits, given that the roadmap item ends when the build succeeds.

The shape shared by most of what broke here is a tool that reports success on an artifact a user cannot run. `codesign --verify` passes ad-hoc signed binaries, because verification checks integrity rather than who signed [9]. `xcrun notarytool submit --wait` returns a clean exit code for a completed upload, not an accepted build, so Apple can answer `status: Invalid` while the script marches on, tries to staple a ticket that was never issued, collects "Record not found" and error 65, and declares the release shipped [11]. Add the script's own success report and that is three places in one pipeline where a green result sits on top of a build that will not install [16].

The identity trap runs the other way. `security find-identity -v -p codesigning` says "0 valid identities found" with the certificate and its private key both sitting in the keychain, because Apple's Developer ID G2 CA intermediate is absent and the trust chain cannot be built; the error text does not mention any of that [5]. The check that actually settles it is `codesign -d -vvv` showing three authority lines, from the Developer ID Application leaf up through the Developer ID Certification Authority to Apple Root CA [6].

What the author ends up building is a distrust of the tools rather than a better one: pick binaries by `file -b` type instead of filename, since Qt and Python framework binaries carry no extension at all and every `find -name "*.dylib"` loop online skips them [7]; use `find -exec` to avoid the argument-length ceiling that a long `Developer ID Application: Name (TEAMID)` string pushes `xargs` into [8]; then grep the authority back out of every Mach-O as a guard [10]. The payoff is arithmetic. A broken build fails on your own machine in 20 seconds instead of 15 minutes into Apple's queue, a loop 45 times shorter on exactly the error class most likely to repeat [15].

The translocation bug is the one no local test reaches. A user who launches from Downloads or a mounted DMG gets a randomized read-only path, so the updater downloaded, installed, relaunched the old version and downloaded again without end, and it never reproduced on the developer's machine because developers do not run their own app out of quarantine [13].

One caution on scope. The account puts Windows antivirus heuristics and browser download blocking next to notarization as the things that consumed weeks [1], but the material here documents the Apple half at error-message granularity and leaves the Windows half asserted. It is one engineer's log of one app, roughly 33k lines of PySide6, MediaPipe and ONNX [3]. The $99 a year Apple Developer fee is the only sum of money in the whole gauntlet; every other cost in it is engineering time [14].

What to watch

  • Whether the Windows half of this gauntlet, antivirus heuristics and browser download blocking, shows the same pattern of tools reporting success on artifacts users cannot run.
  • Whether Apple ever makes notarytool return a nonzero exit code on a status of Invalid, which would remove the need to grep its text output.
  • Whether shipping through a signed installer instead of a zip or DMG removes the translocation update loop or only relocates it.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence58
Adoption24
Hype gap−5
Incentives30
Confidence46
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    Building the app took months; getting it to install and run on other people's machines took weeks of fighting Apple's notarization, Windows antivirus heuristics and browser download blocking, and almost none of it was documented in one place.

  2. [2]

    The paperwork requires an Apple Developer account at $99/year, a Developer ID Application certificate, an app-specific password, and storing credentials once with xcrun notarytool store-credentials.

  3. [3]

    The author built a desktop app in Python using PySide6, MediaPipe and ONNX, about 33k lines, and shipped it to beta users on macOS and Windows.

    ReportedSupportedView cited source

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · August 21, 2026

    Shipping a Python desktop app to real users: everything that breaks after PyInstaller succeeds

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

  • Antivirus Heuristic False PositivesFollow
  • Release Pipelines That Report False SuccessFollow
  • Gatekeeper App TranslocationFollow
  • Python Desktop App PackagingFollow
  • macOS Code Signing and NotarizationFollow
Loading related stories