<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: xxhshenqi</title>
    <description>The latest articles on DEV Community by xxhshenqi (@xxhshenqi).</description>
    <link>https://dev.to/xxhshenqi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4213641%2F282e4676-8121-4a64-bfef-1ef5749669d1.jpg</url>
      <title>DEV Community: xxhshenqi</title>
      <link>https://dev.to/xxhshenqi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xxhshenqi"/>
    <language>en</language>
    <item>
      <title>Apple blocked app-specific-password uploads (error -22938). Here's how to upload IPAs with the official Build Uploads API</title>
      <dc:creator>xxhshenqi</dc:creator>
      <pubDate>Wed, 28 Oct 2026 17:59:42 +0000</pubDate>
      <link>https://dev.to/xxhshenqi/apple-blocked-app-specific-password-uploads-error-22938-heres-how-to-upload-ipas-with-the-5fej</link>
      <guid>https://dev.to/xxhshenqi/apple-blocked-app-specific-password-uploads-error-22938-heres-how-to-upload-ipas-with-the-5fej</guid>
      <description>&lt;p&gt;If you upload your IPA with a tool that authenticates using an Apple ID and an &lt;strong&gt;app-specific password&lt;/strong&gt; (ios-uploader, custom CI scripts, etc.), it stopped working around &lt;strong&gt;October 28, 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The symptom
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Found Bundle ID "com.example.app", Version 125 (1.2.5).
Unexpected token 'S', "Sign in wi"... is not valid JSON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IPA itself is fine — the failure happens at the authentication step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: rule out your credentials
&lt;/h2&gt;

&lt;p&gt;The same app-specific password still authenticates fine against Apple's CalDAV endpoint (HTTP 207). But hitting the upload auth endpoint returns &lt;strong&gt;plain text instead of JSON&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same account, same password, one works and one doesn't. So the credentials are not the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: confirm it's server-side
&lt;/h2&gt;

&lt;p&gt;I tried every variant I could think of — all rejected with the identical message:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;plain and gzip-compressed request bodies&lt;/li&gt;
&lt;li&gt;HTTP/1.1 and HTTP/2&lt;/li&gt;
&lt;li&gt;the JSON-RPC endpoint and the SOAP &lt;code&gt;/ws/&lt;/code&gt; endpoint&lt;/li&gt;
&lt;li&gt;different User-Agents (&lt;code&gt;iTMSTransporter/4.2.0&lt;/code&gt;, &lt;code&gt;4.3.1&lt;/code&gt;, &lt;code&gt;Transporter/5.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;an App Store Connect &lt;strong&gt;API key JWT&lt;/strong&gt; sent as the &lt;code&gt;myacinfo&lt;/code&gt; cookie &lt;em&gt;or&lt;/em&gt; a Bearer token&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;contentdelivery02&lt;/code&gt; host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a nonexistent JSON-RPC method name returns the same login prompt — which means the request never reaches the backend service. It is rejected at the gateway (response headers show &lt;code&gt;Server: daiquiri/5&lt;/code&gt;, instance tag &lt;code&gt;26HOTFIX47&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Apple closed the Apple-ID + app-specific-password login path for this service at the gateway. No client-side parameter tweak will get around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: the official Build Uploads API
&lt;/h2&gt;

&lt;p&gt;App Store Connect has a documented &lt;strong&gt;Build Uploads&lt;/strong&gt; resource that does exactly this, authenticated with an API key (JWT) — it never touches the blocked endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST  /v1/buildUploads            create a build upload (version, build number, platform)
POST  /v1/buildUploadFiles        get presigned object-storage upload URLs
PUT   &amp;lt;presigned-url&amp;gt;             upload the IPA in parts (5 MB chunks)
PATCH /v1/buildUploadFiles/{id}   mark uploaded=true
-&amp;gt; Apple parses the build; it shows up in TestFlight / App Store Connect
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Gotcha: valid &lt;code&gt;assetType&lt;/code&gt; values are &lt;code&gt;ASSET&lt;/code&gt;, &lt;code&gt;ASSET_DESCRIPTION&lt;/code&gt;, and &lt;code&gt;ASSET_SPI&lt;/code&gt; — &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;IPA&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What you need
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;An App Store Connect &lt;strong&gt;API key&lt;/strong&gt;: App Store Connect → Users and Access → Integrations → App Store Connect API → generate a key (role &lt;strong&gt;App Manager&lt;/strong&gt; or higher). Download the &lt;code&gt;.p8&lt;/code&gt; (only downloadable once), note the &lt;strong&gt;Key ID&lt;/strong&gt; and &lt;strong&gt;Issuer ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Your app's numeric &lt;strong&gt;App ID&lt;/strong&gt; (the number after &lt;code&gt;apps/&lt;/code&gt; in the App Store Connect URL).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A working CLI
&lt;/h3&gt;

&lt;p&gt;I packaged this flow into a zero-dependency Node.js CLI (built-in modules only, works on Windows / macOS / Linux):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/xxhshenqi/asc-ipa-uploader" rel="noopener noreferrer"&gt;xxhshenqi/asc-ipa-uploader&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gitee (China mirror): &lt;a href="https://gitee.com/xiaoxianghui/asc-ipa-uploader" rel="noopener noreferrer"&gt;xiaoxianghui/asc-ipa-uploader&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install (no npm account needed, ~2s)&lt;/span&gt;
npm i &lt;span class="nt"&gt;-g&lt;/span&gt; https://github.com/xxhshenqi/asc-ipa-uploader/archive/refs/heads/master.tar.gz

&lt;span class="c"&gt;# look up your App ID&lt;/span&gt;
asc-ipa-uploader &lt;span class="nt"&gt;--key&lt;/span&gt; ./AuthKey_XXXX.p8 &lt;span class="nt"&gt;--key-id&lt;/span&gt; XXXXXXXXXX &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--issuer&lt;/span&gt; &amp;lt;ISSUER_ID&amp;gt; &lt;span class="nt"&gt;--list-apps&lt;/span&gt;

&lt;span class="c"&gt;# upload (version = CFBundleShortVersionString, build = CFBundleVersion)&lt;/span&gt;
asc-ipa-uploader &lt;span class="nt"&gt;--key&lt;/span&gt; ./AuthKey_XXXX.p8 &lt;span class="nt"&gt;--key-id&lt;/span&gt; XXXXXXXXXX &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--issuer&lt;/span&gt; &amp;lt;ISSUER_ID&amp;gt; &lt;span class="nt"&gt;--app&lt;/span&gt; &amp;lt;APP_ID&amp;gt; &lt;span class="nt"&gt;--ipa&lt;/span&gt; ./MyApp.ipa &lt;span class="nt"&gt;--version&lt;/span&gt; 1.2.5 &lt;span class="nt"&gt;--build&lt;/span&gt; 125

&lt;span class="c"&gt;# optionally create the App Store version and attach the build afterwards&lt;/span&gt;
asc-ipa-uploader ... &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things it handles for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;checks whether the same build number already exists before uploading (saves you a pointless 17 MB transfer)&lt;/li&gt;
&lt;li&gt;retries the transient &lt;code&gt;401&lt;/code&gt;s that brand-new API keys produce while Apple propagates them&lt;/li&gt;
&lt;li&gt;uploads in chunks with progress output&lt;/li&gt;
&lt;li&gt;polls until the build reaches &lt;code&gt;VALID&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my test a 17 MB IPA uploaded and reached &lt;code&gt;VALID&lt;/code&gt; in about two minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes / caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Never commit your &lt;code&gt;.p8&lt;/code&gt;. The CLI also accepts env vars: &lt;code&gt;ASC_KEY_PATH&lt;/code&gt;, &lt;code&gt;ASC_KEY_ID&lt;/code&gt;, &lt;code&gt;ASC_ISSUER&lt;/code&gt;, &lt;code&gt;ASC_APP_ID&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Apple rejects duplicate build numbers — bump the build number to re-upload.&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this saves you a day of debugging, a star is appreciated. Issues and PRs welcome.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>appstore</category>
      <category>opensource</category>
      <category>node</category>
    </item>
  </channel>
</rss>
