fix #464: fall back to the redownload endpoint when volumeStore returns 5002#483
Open
koraytutuncu wants to merge 1 commit into
Open
fix #464: fall back to the redownload endpoint when volumeStore returns 5002#483koraytutuncu wants to merge 1 commit into
koraytutuncu wants to merge 1 commit into
Conversation
volumeStore stays the primary download endpoint, so every app that works today keeps hitting the same endpoint. On failureType 5002 it falls back to the bag-resolved redownloadProduct. The redownload response disambiguates a genuine license (redownload serves the app) from a transient volumeStore 5002 (redownload cannot serve it -> retry volumeStore) -- the latter previously made list-versions fail on apps that download succeeded on. - per-endpoint version-pin key: volumeStore uses externalVersionId, redownload uses appExtVrsId - remove the 5002 -> ErrPasswordTokenExpired mapping that caused a re-login loop - surface the endpoint customerMessage on empty results instead of a generic 'invalid response' - download, list-versions and get-version-metadata share sendDownloadProduct + downloadProductItem - volumeStore stays hardcoded: the bag's volumeStoreDownloadProduct URL is the enterprise/VPP host and returns HTTP 500 for the client request shape Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
For a consumer Apple ID,
download,list-versions, andget-version-metadatabreak for any app the account already holds a license for (Microsoft Teams, Office, and others): thevolumeStoreDownloadProductendpoint returnsfailureType 5002.mainadditionally maps5002 → ErrPasswordTokenExpired, which sends the CLI into a re-login loop before failing.The earlier version of this PR replaced
volumeStoreDownloadProductwith the bag'sredownloadProduct. That fixes the licensed apps but breaks the opposite set: apps the account is not licensed for (e.g. YouTube, Instagram) are served byvolumeStorebut rejected byredownloadwith"<App> No Longer Available". Neither endpoint serves every app — so this is a fallback, not a swap.What this does
volumeStorestays the primary endpoint (every app that works today keeps hitting the exact same endpoint — no regression). OnfailureType 5002it falls back to the bag-resolvedredownloadProduct. The redownload response then disambiguates two different causes of 5002:"No Longer Available") → the 5002 was a transient volumeStore hiccup → retry volumeStore.This last point matters:
volumeStorereturns 5002 intermittently even for apps it can serve (≈1-in-8 calls, usually the first), so a naive "any 5002 → redownload" makeslist-versionsfail on an app thatdownloadsucceeds on seconds later. The retry removes that inconsistency.Other changes:
5002 → ErrPasswordTokenExpiredmapping removed (no more re-login loop).volumeStorereadsexternalVersionId,redownloadreadsappExtVrsId(the other key is silently ignored). A single shared helper now sends the request and threads the right key.customerMessage(e.g."No Longer Available") instead of a generic"invalid response".download,list-versions, andget-version-metadatashare onesendDownloadProduct+downloadProductItem(the three POST the same payload and decode the same shape) — net simplification.volumeStorestays hardcoded: the bag advertises avolumeStoreDownloadProductURL too, but it points at the enterprise/VPP host (downloaddispatch…/ent/download) which returns HTTP 500 for the client request shape. OnlyredownloadProductis resolved from the bag.Why not resolve volumeStore from the bag, and why retry instead of switch
The transcript below was produced by a live run against a real consumer Apple ID (account redacted). It shows the bag's volumeStore URL is unusable (HTTP 500), that
5002is deterministic for a licensed app (Teams, 12/12) but intermittent for a servable one (YouTube), and that the version-pin key is endpoint-specific.Verification
go test ./... && go vet ./... && gofmt -lare clean; the fallback is covered by unit specs acrossdownload/list-versions/get-version-metadata(including the tvOS key translation and the transient-5002 recovery). The transcript below also exercises every command end-to-end against a live account.Automated checks
Live end-to-end (real consumer Apple ID, account redacted)
Disclosure: implemented and verified end-to-end with Claude Code against a live test account in one environment.
Summary by cubic
Fixes #464 by adding a safe fallback to the bag’s
redownloadProductwhenvolumeStoreDownloadProductreturns failureType 5002. Restores downloads, list-versions, and version metadata for licensed apps without breaking non-licensed apps.Bug Fixes
volumeStoreprimary; on5002, tryredownloadProduct. IfredownloadProductalso can’t serve (empty failureType + “No Longer Available”), retryvolumeStoreto handle transient 5002s.5002 → ErrPasswordTokenExpiredmapping to stop the re-login loop.customerMessageon empty results.Refactors
sendDownloadProductanddownloadProductItemused bydownload,list-versions, andget-version-metadatainpkg/appstore.externalVersionIdforvolumeStore,appExtVrsIdforredownloadProduct.Bagnow returns theredownloadProductURL;cmd/*fetch the bag and passRedownloadEndpointthrough topkg/appstore.volumeStoreURL hardcoded; the bag’svolumeStoreDownloadProductpoints to VPP and returns HTTP 500 for client requests.Written for commit e8ca429. Summary will update on new commits.