← Blog

The macOS build, and a certificate error that was never about us

One person running the Mac build turned up five separate faults in an afternoon. The certificate one is the interesting one, so it goes first, but all five are fixed in the build on the download page now. If you already downloaded today, take it again.

If you run Options Sniper on a Mac and loading your Logic Pack failed with unable to get local issuer certificate, that is fixed in the latest macOS build. This is what was happening, because the cause is genuinely interesting and it is not what the error suggests.

The symptom that misleads everyone

The app said it could not reach the logic service. Meanwhile the same machine could open the same URL in Safari or Chrome without complaint. That combination makes it look like a server fault, or a licence fault, and it is neither.

It is a trust problem, not a reachability problem. The connection was made. What failed was verifying who answered.

Two causes, stacked

The first is specific to packaged Mac apps. macOS keeps its root certificates in Keychain. OpenSSL, which Python uses underneath, cannot read Keychain — it wants a file. A bundled Mac app therefore ships with no trust store at all unless one is deliberately included. Every HTTPS call fails, every time.

The usual fix is to bundle certifi, a copy of the public web's root certificates. We now do. But on its own that still was not enough, and the reason is the second cause.

Antivirus and VPN software with HTTPS or web scanning turned on intercepts every secure connection. To read traffic it re-signs it with a root certificate of its own, which it installs on your machine. Your browser trusts it, because it reads the same system keychain the antivirus wrote to. An app carrying only certifi does not — that root is private to your computer and will never be in a public bundle. So the app rejects the connection while the browser two inches away is perfectly happy.

What changed

You do not need a new Logic Pack. Your licence was never the problem, and reissuing one would have changed nothing — the pack is a few hundred bytes holding a licence id and an address. If yours failed to load, it is still valid. Update the app and load the same file.

If it still fails

Turn off HTTPS or web scanning in your antivirus and try again. On most products this lives under a web shield or network protection section. The same setting breaks other developer tools on the same machine, so it is worth knowing about regardless of this app.

If that does not do it, reply to the email your pack came in and send the exact error text. It now identifies itself clearly enough to tell us which of these it is.

Four more, from the same afternoon

None of these were visible from a Windows machine, which is the whole lesson of shipping a second platform.

First launch could not find its own config. Logging in failed with config.json not found. The build was bundling the starter config under its example filename, and the app looks for the real one — PyInstaller keeps the source filename, so the template landed under a name nothing reads. The copy step then did nothing and said nothing, and the error surfaced much later at login, where it looks like a licence problem. The app now accepts either name, says why if it still cannot find one, and the build itself fails if the template is missing or is not the neutral one.

The app wrote inside its own bundle. Config, credentials, log and ledger all went next to the executable, which on macOS is inside Options Sniper.app. That is read-only while the app runs from the mounted disk image — which is what most people do before dragging it to Applications — and writing there breaks the signature afterwards. Mac state now lives in ~/Library/Application Support, where it belongs, and survives replacing the app.

The buttons looked broken, and the app had no icon. Tk's native button on macOS ignores background and foreground colour outright, so every button on a dark panel drew as a pale grey chip — not a themed button, a button that looks like it failed to load. macOS now draws its own. And PyInstaller wants an .icns on Mac and quietly discards the Windows .ico it was handed, which is why the app came up with the generic placeholder icon. Both builds now generate a real icon set from the full-resolution shield.

And then login failed with "read-only file system". This one is worth the detail. A Mac app launched from Finder inherits a working directory of / — and on modern macOS the system volume is read-only. The Webull SDK writes three log files by bare relative filename, and resolves its token directory relative as well, so all of them landed on a volume that cannot be written. The connect step reported it as a login failure, which is the one thing it is not: the credentials were never sent. The SDK takes no argument for those paths, so the fix is to anchor the directory they resolve against. Nothing like it can happen on Windows, which has no read-only root — a whole class of bug that only exists on the second platform you ship.

Why this is written down

Because these failure modes punish the obvious guess. Everything about it points at a server or a licence, and both were fine the whole time — the licence in question had never completed a single connection, which is precisely the evidence that it was never the licence. The honest version of a bug report is usually more useful than a changelog line saying "fixed certificate issue". Four of these five were invisible from the machine they were built on, and one person willing to report exactly what he saw found all of them.