Sparkle shows short alerts like "Update Error!" that hide the real cause. This page lists the Sparkle 2 errors developers hit most, what triggers each one in Sparkle's source, and how to fix it. Every error text is quoted from Sparkle 2. It is part of the guide to distributing a Mac app outside the App Store.
Sparkle shows an error alert when the user starts the check, for example with "Check for Updates…", or after Sparkle shows an update to the user. If an automatic check fails before that, the error goes to the log only.
Read Sparkle's log first
The alert only shows a summary. Sparkle logs each failed update with the full chain of underlying errors. Network errors include the URL. Stream the log in Terminal, then trigger the update check:
log stream --predicate 'subsystem == "org.sparkle-project.Sparkle"'
In Console.app, filter on subsystem:org.sparkle-project.Sparkle. Sparkle's helper processes (Autoupdate, Installer, Downloader, Updater) log to the same subsystem.
The commands below use Sparkle's tools (generate_keys, sign_update, generate_appcast) from the bin folder of the Sparkle download.
An error occurred in retrieving update information.
Sparkle could not download your appcast. The full alert is "An error occurred in retrieving update information. Please try again later." Common causes:
- Wrong feed URL. A wrong path returns 404. A wrong host fails the DNS lookup. Open the exact URL from your
Info.plistin a browser. If the app ever called the deprecatedsetFeedURL:, a URL stored in user defaults overridesSUFeedURL, and Sparkle logs a warning. - HTTP instead of HTTPS. App Transport Security blocks plain HTTP, and the log shows error -1022. When the updater starts, Sparkle also logs that the feed URL may need to change to use HTTPS. Serve the feed over HTTPS.
- A sandboxed app without network access. A sandboxed app needs the
com.apple.security.network.cliententitlement, or Sparkle's downloader service, to fetch the feed. See Sparkle sandboxing. - A server error or a bad TLS setup. Sparkle treats any HTTP status of 400 or higher as an error, and the log shows the status code. A TLS failure shows an
NSURLErrorDomaincode such as -1200.
An error occurred while parsing the update feed.
Sparkle downloaded the feed but cannot read it. Either the XML is not valid, or one item is not valid. One bad item rejects the whole feed. The log names the problem, for example an XML parser error for an HTML entity such as ·, No enclosure in feed item, or Feed item lacks sparkle:version element.
Also check that the feed URL returns XML and not an HTML page, such as a login page or a GitHub "blob" page instead of the raw file. Generate the appcast with generate_appcast instead of editing it by hand, and check it with any XML validator.
The update is improperly signed and could not be validated.
The full alert is "The update is improperly signed and could not be validated. Please try again later or contact the app developer." Sparkle downloaded the update, but the EdDSA signature or the code signature does not match. The log usually says:
EdDSA signature does not match. Data of the update being checked is different than data that has been signed, or the public key and the private key are not from the same set.
Sparkle often adds a hint after this line, for example that the expected content length differs from the downloaded file length. That points to cause 1.
Common causes:
- The server sends a different file than the one you signed. A CDN or a cache serves an old archive, or you uploaded the archive again after signing.
- The private key does not match
SUPublicEDKey. You signed with a key from another Mac, another keychain account, or a new key pair. If you use a custom account, pass--account <name>togenerate_keys,sign_update, andgenerate_appcast. - The item has no
sparkle:edSignature. The log saysThe app has an EdDSA public key, but there is no EdDSA signature in the update, so the update will be rejected.This often comes from cause 2: when the keys do not match,generate_appcastprints a warning but still writes the item without a signature and exits with success. - You changed both keys in one release. Sparkle lets you change the EdDSA key or the Developer ID certificate in a release, but not both.
- The archive has no matching app. The archive must contain an app with the same file name as the installed app, or with the same bundle identifier. The log then says
No suitable install is found in the update. The update will be rejected.
To find the cause, compare your public key with the one in the app, then check the file that your server actually sends:
./bin/generate_keys -p
curl -L -o downloaded.zip "https://example.com/MyApp-2.0.zip"
./bin/sign_update --verify downloaded.zip "<sparkle:edSignature from your appcast>"
sign_update --verify prints nothing if the signature is valid. It checks against your private key, not against the app, so the first command must print the same key as SUPublicEDKey in the app.
If the verification fails, sign and generate the appcast again, upload both, and clear your CDN cache. Do not generate a new key pair to fix this. If you must change keys, follow Sparkle's key rotation rules: in one release, change the EdDSA key or the Developer ID certificate, not both. Sign the release that changes the EdDSA key with the new private key. See Key Management.
If you use SURequireSignedFeed, a similar message says "The update feed is improperly signed". Run generate_appcast or sign_update on the appcast again after each manual edit.
An error occurred while downloading the update.
Sparkle found the update but could not download the archive in the enclosure URL. Check that the URL returns the file with status 200, uses HTTPS, and is reachable without a login. On S3 or a CDN, a 403 usually means the object is private or a presigned URL expired. If the log mentions the downloader service, see Sparkle sandboxing: apps that are not sandboxed, or that have the com.apple.security.network.client entitlement, should not enable SUEnableDownloaderService.
An error occurred while extracting the archive.
Sparkle downloaded the update but could not unpack it. Sparkle supports ZIP, DMG, tar (.tar, .tar.gz, .tar.bz2, .tar.xz), Apple Archive, and .pkg. It picks the format from the file extension of the downloaded file name. Check that:
- the file at the enclosure URL is the archive, not an HTML error page,
- the server sends a file name with the correct extension. A URL with no extension, or a wrong
Content-Dispositionheader, givesNo valid unarchiver was foundin the log, - the DMG mounts when you open it, and
- for
.aaror.yaaarchives, the installed app hasSUVerifyUpdateBeforeExtractionset toYESand anSUPublicEDKey. Setting it only in the new release does not help.
If a delta update fails to extract, Sparkle uses the full archive without an alert.
An error occurred while launching the installer.
Sparkle could not start its installer. The log says Error: Failed to gain authorization required to update target. The line before it gives the cause:
Failed to submit installer job: a sandboxed app cannot start the installer. SetSUEnableInstallerLauncherServicetoYESinInfo.plistand follow Sparkle sandboxing.InstallerLauncher failed to create bundle: the app was signed withcodesign --deep, so the helpers got the app's sandbox entitlements. Do not use--deep. If you do not use Xcode's Archive and Export, signInstaller.xpcand the other helpers yourself. See Sparkle helpers.Failed copying system domain rights: the update needs administrator rights, for example because another user owns the app, and macOS did not grant them.
Related installer messages:
- "An error occurred while connecting to the installer."
SUEnableInstallerLauncherServiceis set in an app that is not sandboxed, orInstaller.xpclost its executable permission. - "An error occurred while running the updater." In a sandboxed app, the mach-lookup exception is usually missing. Other causes are helpers that lost their executable permission, or helpers with a different team ID than the app. To add the exception, add
com.apple.security.temporary-exception.mach-lookup.global-namewith the values$(PRODUCT_BUNDLE_IDENTIFIER)-spksand$(PRODUCT_BUNDLE_IDENTIFIER)-spkito your entitlements. - "An error occurred while starting the installer." The installer did not start within 7 seconds. The log says
Timeout: Installer never started archive extraction.
You’re up to date! (but an update exists)
Sparkle found no update that applies to this installation. Check these, in this order:
CFBundleVersion. Sparkle comparessparkle:versionin the appcast with the build number (CFBundleVersion), not with the marketing version. Increase the build number for every release.- Channels. An item with
sparkle:channelis only offered if your updater delegate returns that channel inallowedChannelsForUpdater:. - Minimum update version.
sparkle:minimumUpdateVersionis higher than the installed version. - An old feed URL. If the app ever called the deprecated
setFeedURL:, that URL stays in user defaults and overridesSUFeedURL. Sparkle logs a warning about it. - A cached appcast. Your CDN serves an old copy of the appcast. Open the feed URL and check that the new item is there.
Phased rollout does not cause this alert, because a manual check ignores it. It only delays automatic checks, so an automatic check can find no update while a manual check finds one.
The generate_appcast options that control these are --channel, --minimum-update-version, and --phased-rollout-interval.
Your macOS version is too old
The update requires a newer macOS than the user has. The alert says the version is available but "your macOS version is too old to install it". generate_appcast copies LSMinimumSystemVersion from your app into sparkle:minimumSystemVersion. Related alerts are "Your macOS version is too new" (from sparkle:maximumSystemVersion) and "Your Mac is too old", which appears on Intel Macs when the item sets sparkle:hardwareRequirements to arm64 or requires macOS 27 or later. generate_appcast adds arm64 when your app has no Intel code.
MyApp can’t be updated if it’s running from the location it was downloaded to.
macOS runs the app from a temporary, randomized location (App Translocation) because the user opened the downloaded app without moving it first, for example from the Downloads folder. The alert asks the user to move the app into the Applications folder and relaunch it.
A similar alert, "MyApp can’t be updated because it was opened from a read-only or a temporary location.", usually appears when the user runs the app directly from the mounted DMG.
To prevent both, put an /Applications shortcut in your DMG so users drag the app there. See DMG creation.
Unable to Check For Updates
The alert says "The updater failed to start." Sparkle refused to start, usually because of the EdDSA public key:
SUPublicEDKeyis missing and the feed uses HTTP or the app is not code signed. Sparkle logsFor security reasons, updates need to be signed with an EdDSA key.SUPublicEDKeyis not valid. Sparkle logsThe EdDSA public key is not valid. Copy the key again from./bin/generate_keys -p.SURequireSignedFeedwithoutSUVerifyUpdateBeforeExtraction. Set both toYES.- An XPC service is enabled but missing.
SUEnableInstallerLauncherServiceorSUEnableDownloaderServiceisYES, but the service is not in the Sparkle framework, or you copied it into the app bundle yourself. See Sparkle sandboxing.
If the key in the app does not match the key in your keychain, generate_appcast prints Warning: SUPublicEDKey in the app ... does not match key EdDSA in the Keychain. It still writes the appcast, but without sparkle:edSignature for that item. Fix this before you publish.
A missing SUFeedURL does not stop the updater from starting. A manual check then shows "Update Error!" and the log says You must specify the URL of the appcast.
To use your private key in CI, export it once, store the contents of private-key.txt as a secret, and pass it to generate_appcast. Use --ed-key-file, not -f: in generate_appcast, -f is the old DSA key.
./bin/generate_keys -x private-key.txt
echo "$SPARKLE_PRIVATE_KEY" | ./bin/generate_appcast --ed-key-file - ./updates
Skip the manual steps
Amore generates the EdDSA keys, signs every update, writes the appcast, and hosts it on HTTPS. Start with Get Started, or see Key Management to import the key you already use.