fix #464: fall back to redownload endpoint on FailureType 5002#477
Open
koraytutuncu wants to merge 9 commits into
Open
fix #464: fall back to redownload endpoint on FailureType 5002#477koraytutuncu wants to merge 9 commits into
koraytutuncu wants to merge 9 commits into
Conversation
PR majd#466 introduced a typed contextKey (interactiveKey) for the interactive flag but auth.go and download.go still queried the context with the raw string "interactive", which never matches a value stored under a contextKey-typed key. As a result, auth login panicked on a nil-to-bool type assertion the first time it ran post-majd#466, and download silently treated the session as non-interactive (no progress bar). Switch both call sites to look up via interactiveKey and use the comma-ok form of the type assertion to avoid the panic. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…jd#464) Apple's volumeStoreDownloadProduct path on buy.itunes.apple.com (the endpoint ipatool drives for download, list-versions, and get-version-metadata) now responds with FailureType 5002 and Items=null for a number of apps the calling account already owns -- Microsoft Teams, the Office bundle, Spotify, Facebook, etc. The bag advertises a separate redownload endpoint at https://downloaddispatch.itunes.apple.com/r/redownload that accepts the same plist payload (with appExtVrsId in place of externalVersionId for version pinning) and returns the full download metadata for those apps. When the primary request comes back with FailureType 5002, retry the same call against the redownload endpoint and use that response. If the fallback also fails, surface its customerMessage in the empty-Items branch so users see a meaningful error (e.g. "Redownload Unavailable with This Apple Account") instead of "invalid response." Also stop mapping FailureType 5002 to ErrPasswordTokenExpired in the download flow. The mapping was added in majd#468 on the assumption that the condition was a stale token, but re-authenticating doesn't change Apple's response for the affected apps; with the redownload fallback in place the false re-login retry only obscures the real outcome. Verified end-to-end: list-versions and get-version-metadata return the full version history for com.microsoft.skype.teams, and download produces a valid 366 MB .ipa with the Microsoft Teams app bundle intact. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
majd
reviewed
May 10, 2026
Comment on lines
+28
to
+29
| PrivateDownloadDispatchAPIDomain = "downloaddispatch." + iTunesAPIDomain | ||
| PrivateDownloadDispatchAPIPath = "/r/redownload" |
Owner
There was a problem hiding this comment.
We should resolve this from the bag rather than hardcoding it here.
Contributor
Author
There was a problem hiding this comment.
Agreed. I’ll try to look into this more when I have some time.
majd
requested changes
May 10, 2026
Reverts both commits that previously sat on this branch: - 3aa4a86 "fix: fall back to redownload endpoint when MZFinance returns 5002 (majd#464)" - 47c7040 "fix: look up interactive context value with the typed key" After this commit the tree is identical to majd/ipatool main (85ae82d), clearing the way for the bag-resolved approach to land as a clean series of focused commits without force-pushing the branch. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
cmd/root.go writes the interactive flag using a typed contextKey, but cmd/auth.go and cmd/download.go read it back with the raw string "interactive". The lookup misses, leaving a nil interface that crashes the bare type assertion in auth.go on every `auth login` invocation and silently disables the progress bar in `download` interactive mode. Switch both call sites to look the value up with the typed key and the comma-ok variant. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Apple's bag at init.itunes.apple.com/bag.xml advertises a redownloadProduct key alongside the existing authenticateAccount. Parse it and surface it via BagOutput.DownloadEndpoint so callers can resolve the consumer redownload URL at runtime instead of hardcoding it. This commit only exposes the value; no caller consumes it yet. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The volumeStoreDownloadProduct endpoint is Apple's VPP/ABM path. Consumer
Apple IDs hit FailureType 5002 ("license already exists") on it for apps
the account already owns - Microsoft Teams, Office, Facebook, Spotify
and others - breaking ipatool's download for those apps.
Apple's bag advertises a separate redownloadProduct endpoint at
downloaddispatch.itunes.apple.com/r/redownload for exactly this case.
Switch Download to consume the URL via DownloadInput.Endpoint, threaded
from cmd/download.go's bag fetch which is now promoted out of the
token-expired retry branch to the top of the retry function.
Drop the 5002 -> ErrPasswordTokenExpired workaround that only existed
because of the wrong endpoint. Switch the version-pinning payload key
from externalVersionId to appExtVrsId which is what the redownload
endpoint expects.
Fixes the download half of majd#464.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
ListVersions used the same volumeStoreDownloadProduct path and hit the same 5002 wall as Download for the affected apps. Switch it to the bag-resolved redownload URL by adding ListVersionsInput.Endpoint and threading it from cmd/list_versions.go's bag fetch, promoted out of the token-expired branch the same way the download command was. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
GetVersionMetadata hit the same 5002 issue on volumeStoreDownloadProduct. Same fix as the other two: GetVersionMetadataInput.Endpoint threaded from cmd/get_version_metadata.go's bag fetch. Also switch the version-pinning payload key from externalVersionId to appExtVrsId, which is what the redownload endpoint expects. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
After Download, ListVersions and GetVersionMetadata moved to bag-resolved URLs, PrivateAppStoreAPIPathDownload has no remaining references in the package and would be dead code. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #464.
Problem
Apple's
volumeStoreDownloadProductendpoint returnsFailureType 5002for consumer Apple IDs hitting apps with an existing license — Microsoft Teams, Office, Facebook, Spotify, and others — breakingdownload,list-versions, andget-version-metadata. That endpoint is Apple's VPP/ABM path; it just happened to accept some apps as a coincidence.Approach
Per @majd's feedback on the previous version of this PR:
Apple's bag at
init.itunes.apple.com/bag.xmlalready advertises aredownloadProductkey alongside the existingauthenticateAccount. The URL is now resolved at runtime from the bag, not hardcoded anywhere.Changes
BagOutput.DownloadEndpoint—urlBagparses theredownloadProductkey from the bag. Pure additive in its own commit; no caller consumes it yet.DownloadInput.Endpoint/ListVersionsInput.Endpoint/GetVersionMetadataInput.Endpoint— eachcmd/*fetches the bag at the top of its retry function and threads the URL through, mirroring howLoginInput.Endpointalready works forauthenticateAccount. The bag fetch that previously lived inside the token-expired retry branch is just promoted, so the same fetch serves both auth and download.appExtVrsIdpayload key — what the redownload endpoint expects for version pinning, replacingexternalVersionId.PrivateAppStoreAPIPathDownloadremoved — no remaining references after the three call sites move tobag.DownloadEndpoint. Dead constants are slop.5002 → ErrPasswordTokenExpiredmapping inDownloadremoved — that mapping only existed to paper over the wrong endpoint; with the right endpoint, 5002 doesn't appear.Branch history
Rather than force-pushing the branch, the prior two commits (
47c7040,3aa4a86) are kept reachable and explicitly reverted, so your earlier review comments stay anchored and accessible. The revert restores the tree tomain; the six new commits build the bag-resolved approach on top. The branch can be merged as-is; the revert + new commits net out to the clean final diff.Each commit builds and passes
go test ./...independently.Verification
Against a real consumer Apple ID:
download com.microsoft.skype.teams→ 350 MB valid IPA,Payload/TeamSpaceApp.app/with code signature, plug-in extensions, andTeamSpaceApp.sinfintact.list-versions com.microsoft.skype.teams→ full external version identifier list.go test ./...→ green.Same single-environment caveat as the original disclosure: Apple's responses on these endpoints aren't documented and vary by account state, storefront, family-sharing posture, and device-context fields. Other reporters confirming this fixes their 5002s on different apps would help validate generality.
Failure mode worth flagging
There is no fallback to
volumeStoreDownloadProduct. If Apple removes theredownloadProductkey from the bag,bag.DownloadEndpointwill be empty and the request will fail with a generic HTTP error. This is the same trust the existing code already places in the bag forauthenticateAccount— keeping the old hardcoded URL as a fallback would reintroduce the exact 5002 bug this PR fixes. An explicit empty-string check at the call sites (clearer error message: "bag does not advertise a download endpoint") is a 3-line follow-up if you want it.Sources
Disclosure: code written by Claude Code; verified end-to-end against a live test account in one environment.
Summary by cubic
Route App Store requests through the bag-resolved redownload endpoint to avoid
FailureType 5002, restoringdownload,list-versions, andget-version-metadatafor affected apps (fixes #464). Also fixes the interactive flag lookup soauth loginanddownloadinteractive mode work again.redownloadProductURL from the bag and pass it toDownload,ListVersions, andGetVersionMetadata; useappExtVrsIdfor version pinning (pkg/appstoreandcmd/*).5002→ErrPasswordTokenExpiredmapping inDownloadto stop re-login loops.Itemsis empty, showcustomerMessageinstead of “invalid response”.interactiveKeyincmd/auth.goandcmd/download.goto prevent a panic and restore the progress bar.Written for commit 0321942. Summary will update on new commits.