If you upload your IPA with a tool that authenticates using an Apple ID and an app-specific password (ios-uploader, custom CI scripts, etc.), it stopped working around October 28, 2026.
The symptom
Found Bundle ID "com.example.app", Version 125 (1.2.5).
Unexpected token 'S', "Sign in wi"... is not valid JSON
The IPA itself is fine — the failure happens at the authentication step.
Step 1: rule out your credentials
The same app-specific password still authenticates fine against Apple's CalDAV endpoint (HTTP 207). But hitting the upload auth endpoint returns plain text instead of JSON:
Sign in with the app-specific password you generated. If you forgot the
app-specific password or need to create a new one, go to account.apple.com. (-22938)
Same account, same password, one works and one doesn't. So the credentials are not the problem.
Step 2: find the real cause (my first guess was wrong)
I tried every variant I could think of — gzip, HTTP/2, different endpoints, an API-key JWT as cookie or Bearer — and all were rejected with the identical message. So I concluded "Apple closed this channel".
The real cause: Apple tightened the User-Agent validation. It must match a specific full format:
iTMSTransporter/4.3.0.4 (Windows 11; 10.0; amd64; Java 21.0.5) SmartClient/0.1.0
With that UA, login works again — same app-specific password:
[old UA] iTMSTransporter/4.2.0 -> rejected
[new UA] iTMSTransporter/4.3.0.4 (Windows 11;...) -> SessionId returned, login OK
The error message only says "sign in with an app-specific password" and never mentions the UA — extremely misleading. The ios-uploader maintainer hit the same wall and shipped v4.0.3 with the fix.
The real takeaway: these tools depend on Apple's undocumented internal endpoints and "magic strings". Apple can change one silently with zero notice, and you get to guess again.
A more durable approach: the official Build Uploads API
To be clear — for occasional uploads, ios-uploader v4.0.3 is fixed, just keep using it. The approach below is for when the upload path has to be dependable long-term: CI automation, shared team tooling, and not re-guessing Apple's internals every time.
App Store Connect has a documented Build Uploads resource that does exactly this. Authentication uses an API key (JWT) and the contract is public — no reliance on hidden conventions like UA formats:
POST /v1/buildUploads create a build upload (version, build number, platform)
POST /v1/buildUploadFiles get presigned object-storage upload URLs
PUT <presigned-url> upload the IPA in parts (5 MB chunks)
PATCH /v1/buildUploadFiles/{id} mark uploaded=true
-> Apple parses the build; it shows up in TestFlight / App Store Connect
Gotcha: valid
assetTypevalues areASSET,ASSET_DESCRIPTION, andASSET_SPI— notIPA.
What you need
- An App Store Connect API key: App Store Connect → Users and Access → Integrations → App Store Connect API → generate a key (role App Manager or higher). Download the
.p8(only downloadable once), note the Key ID and Issuer ID. - Your app's numeric App ID (the number after
apps/in the App Store Connect URL).
A working CLI
I packaged this flow into a zero-dependency Node.js CLI (built-in modules only, works on Windows / macOS / Linux):
- GitHub: xxhshenqi/asc-ipa-uploader
- Gitee (China mirror): xiaoxianghui/asc-ipa-uploader
# install (no npm account needed, ~2s)
npm i -g https://github.com/xxhshenqi/asc-ipa-uploader/archive/refs/heads/master.tar.gz
# look up your App ID
asc-ipa-uploader --key ./AuthKey_XXXX.p8 --key-id XXXXXXXXXX \
--issuer <ISSUER_ID> --list-apps
# upload (version = CFBundleShortVersionString, build = CFBundleVersion)
asc-ipa-uploader --key ./AuthKey_XXXX.p8 --key-id XXXXXXXXXX \
--issuer <ISSUER_ID> --app <APP_ID> --ipa ./MyApp.ipa --version 1.2.5 --build 125
# optionally create the App Store version and attach the build afterwards
asc-ipa-uploader ... --release
A few things it handles for you:
- checks whether the same build number already exists before uploading (saves you a pointless 17 MB transfer)
- retries the transient
401s that brand-new API keys produce while Apple propagates them - uploads in chunks with progress output
- polls until the build reaches
VALID
In my test a 17 MB IPA uploaded and reached VALID in about two minutes.
Notes / caveats
- Never commit your
.p8. The CLI also accepts env vars:ASC_KEY_PATH,ASC_KEY_ID,ASC_ISSUER,ASC_APP_ID. - Apple rejects duplicate build numbers — bump the build number to re-upload.
- If you have a Mac and use Xcode or the official Transporter app, you are unaffected by this change. The breakage hits Windows/Linux CI pipelines and anyone uploading without a Mac.
If this saves you a day of debugging, a star is appreciated. Issues and PRs welcome.
Top comments (0)