# Lỗi eas submit iOS thường gặp và cách sửa

> eas submit thất bại? Thông báo thật của EAS và App Store Connect, nguyên nhân và cách sửa: ascAppId, khóa API, hợp đồng Apple, mã ITMS.

Trang gốc: [Lỗi eas submit iOS thường gặp và cách sửa](https://devoria.fr/vi/blog/loi-eas-submit-ios-cach-sua)
Tác giả: [Mehdi Azizi](https://devoria.fr/vi/mehdi-azizi)
Ngày đăng: 2026-11-04
Lần sửa nội dung gần nhất: 2026-11-04


Khi `eas submit --platform ios` thất bại, thông báo lỗi gần như luôn cho bạn biết cần xem ở đâu. **Hoặc cấu hình EAS của bạn thiếu, hoặc tài khoản Apple đang chặn, hoặc Apple từ chối file binary.** Hai trường hợp đầu sửa trong vài phút, không cần build lại. Trường hợp thứ ba nhận ra nhờ mã ITMS, và gần như luôn cần một bản build mới.

Mình phát hành các app iOS của mình, trong đó có Solys, TradaZen và Nolera, bằng Expo và EAS. Các thông báo trích dẫn ở đây được chép từ mã nguồn của EAS CLI, trên [kho GitHub chính thức của Expo](https://github.com/expo/eas-cli), đọc lại ngày 3 tháng 11 năm 2026. Diễn đàn cũ của Expo giờ chuyển hướng sang Discord: mã nguồn vẫn là nguồn đáng tin nhất. Để xem toàn bộ quy trình, từ file `eas.json` đến lúc gửi duyệt, hãy đọc trước [EAS Submit iOS: phát hành app Expo lên App Store](https://devoria.fr/vi/blog/eas-submit-ios-dua-app-expo-len-app-store). Ở đây, mình tập trung sửa lỗi.

## eas submit làm gì, và nó cần gì

Theo [tài liệu của Expo](https://docs.expo.dev/deploy/submit-to-app-stores/), lệnh này gửi file `.ipa` của bạn lên App Store Connect. Bản build xuất hiện trong TestFlight sau khi Apple xử lý xong, thường mất 10 đến 15 phút. **Lệnh này không gửi app của bạn đi duyệt**: bước đó bạn vẫn phải làm trong App Store Connect.

[Trang của Expo về việc gửi iOS](https://docs.expo.dev/submit/ios/) liệt kê ba điều kiện:

- **một tài khoản Apple Developer trả phí**, bắt buộc cho mọi lần gửi lên App Store;
- **bundle identifier** trong `ios.bundleIdentifier`;
- **EAS CLI đã cài đặt** và đăng nhập vào tài khoản Expo của bạn.

Để gửi mà không cần tương tác, cùng trang đó thêm hai thiết lập:

- **`ascAppId`** trong profile `submit` của `eas.json`, là số định danh của trang app trên App Store Connect;
- **một khóa API App Store Connect**, do EAS quản lý hoặc do bạn tự cung cấp. Cách tạo, vai trò và cách thu hồi khóa được trình bày chi tiết trong [khóa API App Store Connect (.p8)](https://devoria.fr/vi/blog/khoa-api-app-store-connect-p8).

Profile tối thiểu chỉ vài dòng. Giá trị dưới đây là giả:

```json
{
  "submit": {
    "production": {
      "ios": {
        "ascAppId": "1234567890"
      }
    }
  }
}
```

Nếu EAS đã giữ khóa API của bạn, profile này là đủ. Phần lớn app của mình đều như vậy.

## Đọc thông báo lỗi: ba nhóm lỗi

Trước khi tìm cách sửa, hãy xác định lệnh dừng ở thời điểm nào. Bảng này là khung mình áp dụng mỗi lần thất bại.

| Thời điểm thất bại               | Nhóm lỗi          | Cần build mới?  |
| -------------------------------- | ----------------- | --------------- |
| Trước khi gửi bất cứ thứ gì      | Cấu hình EAS      | Không           |
| Kết nối với Apple, tạo trang app | Tài khoản Apple   | Không           |
| Sau khi gửi, mã ITMS-90xxx       | Binary bị từ chối | Gần như luôn có |

Chi tiết nằm trên trang của lần submit, trong dashboard của Expo. [Expo khuyên](https://docs.expo.dev/deploy/submit-to-app-stores/) đọc log ở đó và tìm khung “Build Annotations”, nơi báo các nguyên nhân thất bại thường gặp. Trong terminal, tùy chọn `--verbose` luôn hiển thị log của EAS Submit, theo [tài liệu tham chiếu EAS CLI](https://docs.expo.dev/eas/cli/).

## Lỗi cấu hình EAS

### “Set ascAppId in the submit profile (eas.json) or re-run this command in interactive mode.”

Bạn chạy lệnh với `--non-interactive`, ví dụ trong CI, và profile của bạn không có `ascAppId`. [Mã của EAS CLI](https://github.com/expo/eas-cli/blob/main/packages/eas-cli/src/submit/ios/IosSubmitCommand.ts) khi đó sẽ dừng: nó không thể hỏi bạn, cũng không thể tạo trang app thay bạn.

**Cách sửa: chép số định danh trang app vào `ascAppId`.** [Expo chỉ chỗ tìm](https://github.com/expo/fyi/blob/main/asc-app-id.md): trong App Store Connect, mở app của bạn, rồi vào App Information. Đó là trường Apple ID, dưới General Information, một con số khoảng mười chữ số. Nó không phải email Apple của bạn, cũng không phải bundle identifier.

Với Nolera, khi chưa có `ascAppId`, lệnh cứ đòi mình một phiên đăng nhập Apple Developer, mà phiên này thì hết hạn. Từ khi có số này trong `eas.json`, việc gửi không hỏi gì nữa.

> Hãy thêm `ascAppId` trước khi build, không phải sau. Cho đến SDK 57, [fingerprint do Expo tính](https://github.com/expo/expo/blob/main/packages/@expo/fingerprint/src/sourcer/Expo.ts) luôn bao gồm `eas.json`. Với Vigie, có runtime version theo chính sách `fingerprint`, việc thêm số này sau khi build đã làm thay đổi fingerprint: một bản update phát hành sau đó sẽ không bao giờ đến được bản build đã cài. Từ SDK 58, preset mặc định của fingerprint, `balanced`, bỏ qua `eas.json`, theo [changelog của Expo](https://github.com/expo/expo/blob/main/packages/@expo/fingerprint/CHANGELOG.md).

### “App Store Connect API Keys cannot be set up in --non-interactive mode.”

Profile của bạn không có khóa, và EAS cũng không có khóa nào gắn với app của bạn. [Expo nói rõ](https://docs.expo.dev/submit/ios/): ở chế độ không tương tác, lệnh không thể hỏi bạn khóa, nên thất bại với thông báo này.

Có hai cách sửa:

- **Để EAS tạo và giữ khóa.** Chạy một lần `eas credentials --platform ios` ở chế độ tương tác trên máy của bạn, và để nó cấu hình một khóa API App Store Connect cho app.
- **Tự cung cấp khóa** trong `ascApiKeyPath`, `ascApiKeyIssuerId` và `ascApiKeyId`. Theo [tài liệu tham chiếu eas.json](https://docs.expo.dev/eas/json/), ba trường này nhận biến môi trường, như `$ASC_API_KEY_PATH`.

Ở app gần nhất của mình, tùy chọn `--auto-submit` đòi Apple ID để tạo khóa gửi. Mình đã đặt ba trường của khóa riêng vào profile trong bản làm việc local, không bao giờ commit. Sau đó mình gửi bản build đã xong bằng `--id` và `--non-interactive`.

### “EXPO_APPLE_APP_SPECIFIC_PASSWORD must be in the format xxxx-xxxx-xxxx-xxxx, where x is a lowercase letter.”

Biến này chứa thứ khác ngoài mật khẩu dành riêng cho app, thường chính là mật khẩu Apple của bạn. [EAS CLI](https://github.com/expo/eas-cli/blob/main/packages/eas-cli/src/submit/ios/IosSubmitCommand.ts) kiểm tra định dạng này trước khi gửi.

Theo [trang hỗ trợ của Apple](https://support.apple.com/en-us/102654), mật khẩu dành riêng cho app được tạo trên account.apple.com, mục App-Specific Passwords, khi đã bật xác thực hai yếu tố. Nó bị thu hồi ngay khi bạn đổi mật khẩu tài khoản Apple. **Expo đánh giá phương pháp này [kém an toàn hơn khóa API](https://github.com/expo/fyi/blob/main/apple-app-specific-password.md), phương pháp mặc định và được khuyên dùng.**

## Lỗi tài khoản Apple

### “… agree to the latest Program License Agreement in your developer account.”

Thông báo đầy đủ bắt đầu bằng “You currently don't have access to this membership resource”. Phía Apple, phản hồi gốc có thể chứa “Unable to process request - PLA Update available”. Các bước gửi mới của EAS thì thường hiển thị “A required Apple agreement is missing or has expired”.

Nguyên nhân giống nhau: Apple đã cập nhật một hợp đồng, và chưa ai chấp nhận. **Chỉ chủ tài khoản, tức Account Holder, mới chấp nhận được hợp đồng**, theo [bảng vai trò của Apple](https://developer.apple.com/help/account/access/roles/). [Mã của EAS](https://github.com/expo/eas-cli/blob/main/packages/build-tools/src/steps/utils/ios/AscApiClient.ts) yêu cầu người này vào trang Business của App Store Connect, rồi nếu Apple đề nghị, vào tài khoản của họ trên developer.apple.com. Sau đó chạy lại lệnh. Hợp đồng cho app trả phí có hướng dẫn riêng: [Paid Apps Agreement trong App Store Connect](https://devoria.fr/vi/blog/paid-apps-agreement-trang-thai-w-8ben-nhan-tien).

### “App Store Connect rejected the API authentication credentials.”

Apple trả về lỗi 401, `NOT_AUTHORIZED`. [EAS](https://github.com/expo/eas-cli/blob/main/packages/build-tools/src/steps/utils/ios/AscApiClient.ts) nêu ba nguyên nhân: khóa không còn hoạt động, khóa thuộc một tài khoản App Store Connect khác, hoặc Key ID, nội dung khóa hay Issuer ID bị sai.

Kiểm tra hai định danh trong App Store Connect, menu Users and Access, tab Integrations. Theo [Apple](https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests), Issuer ID hiển thị ở đầu trang, còn Key ID nằm trong danh sách khóa đang hoạt động. **Khóa đã thu hồi không bao giờ kích hoạt lại được.** [Apple hiển thị nó](https://developer.apple.com/documentation/appstoreconnectapi/revoking-api-keys) 30 ngày dưới mục Revoked: bạn phải tạo khóa mới.

### “App Store Connect app for application identifier … was not found.”

Không tìm thấy trang app mà `ascAppId` trỏ tới với khóa của bạn. [EAS](https://github.com/expo/eas-cli/blob/main/packages/build-tools/src/steps/utils/ios/AscApiUtils.ts) yêu cầu bạn kiểm tra lại số này, và kiểm tra khóa có quyền truy cập app trong đúng tài khoản. Nó còn liệt kê vài app mà khóa này thấy được. Với công cụ gửi của Apple, cùng vấn đề này hiện ra dưới dạng “No suitable application records were found”.

Có ba nguyên nhân hay gặp, trong đó hai cái được nêu trong [luồng thảo luận này trên diễn đàn Apple](https://developer.apple.com/forums/thread/106806):

- **Trang app chưa tồn tại.** Hãy tạo nó trong App Store Connect, hoặc bỏ `ascAppId` để lệnh tự tạo.
- **Số này đến từ tài khoản khác.** Bạn đã chép `ascAppId` của một team khác, hoặc của một dự án khác.
- **Người dùng không có quyền truy cập app.** Một App Manager hay Developer có thể bị giới hạn ở một số app. Còn khóa team thì [truy cập được mọi app](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api) của tài khoản.

### “… is not available to team” hoặc “… is not available to provider”

Thông báo này xuất hiện khi EAS tạo trang app thay bạn, vì không có `ascAppId`. Bundle identifier đã bị một team khác dùng. [EAS yêu cầu bạn](https://github.com/expo/eas-cli/blob/main/packages/eas-cli/src/credentials/ios/appstore/ensureAppExists.ts) đổi nó trong cấu hình app, rồi chạy lại.

### “App name "…" is already taken. Using generated name "…"”

Đây không phải lỗi. Khi tên đã có người dùng trên App Store, EAS thêm sáu ký tự ngẫu nhiên và thử lại, tối đa năm lần. Bạn sẽ đổi tên trang app sau trong App Store Connect. Để tránh chuyện này, hãy điền `appName` và `language` trong profile trước lần gửi đầu tiên: nếu không có `language`, [ngôn ngữ chính mặc định là `en-US`](https://docs.expo.dev/eas/json/).

## Lỗi binary: các mã ITMS

Sau khi gửi, Apple xử lý bản build. Nếu bị từ chối, log sẽ nêu một hoặc nhiều mã ITMS. Đây là những mã mà [bước gửi của EAS](https://github.com/expo/eas-cli/blob/main/packages/build-tools/src/steps/functions/uploadToAsc.ts) nhận diện và giải thích:

| Mã ITMS                    | Apple từ chối điều gì                   | Cách sửa                |
| -------------------------- | --------------------------------------- | ----------------------- |
| 90189                      | Số build đã được dùng                   | `autoIncrement`         |
| 90062, 90186, 90478        | Version đã đóng hoặc đã bị vượt qua     | Tăng `version`          |
| 90683                      | Thiếu câu giải thích quyền truy cập     | `ios.infoPlist`         |
| 90725                      | SDK iOS quá cũ                          | Image iOS hoặc SDK Expo |
| 90054, 90055               | Bundle khác với trang app được nhắm tới | Sửa `ascAppId`          |
| 90022, 90023, 90391, 90713 | Icon thiếu hoặc khai báo sai            | Xem lại icon            |
| 90771                      | Thiếu định danh tác vụ nền              | `ios.infoPlist`         |

Ba lưu ý rút ra từ các thông báo này:

- **Số build là duy nhất cho mỗi version.** Với 90189, EAS khuyên đặt `"autoIncrement": true` trong `eas.json`. Mọi thứ có trong [số build và version](https://devoria.fr/vi/blog/build-number-app-store-connect-version).
- **Với 90725**, EAS khuyên chọn image iOS mới hơn trong `eas.json`, và nếu cần thì chuyển sang một SDK Expo còn được hỗ trợ.
- **Với 90771**, Info.plist của bạn khai báo background mode `processing`, nhưng không có khóa `BGTaskSchedulerPermittedIdentifiers`.

Trừ 90054 và 90055, nơi chỉ cần nhắm đúng trang app hoặc một bản build khác, **cách sửa đụng đến binary: build lại, rồi gửi lại.** Hai mã có hướng dẫn riêng: [ITMS-90683 với Expo](https://devoria.fr/vi/blog/itms-90683-missing-purpose-string-expo) và [ITMS-90725](https://devoria.fr/vi/blog/itms-90725-sdk-version-issue-expo).

Cuối tháng 9 năm 2026, một bản build của Solys bị từ chối lúc gửi vì ITMS-90683. Mình đã xóa một câu giải thích quyền truy cập ảnh khi dọn file cấu hình. Solys không dùng ảnh, nhưng một thư viện trong dự án có gọi API Photos của iOS. **Apple phân tích binary, không phải cách dùng thực tế:** từ đó, mình không bao giờ xóa những câu này nữa.

Còn trạng thái Missing Compliance thì không phải lỗi gửi. Nó thiếu phần khai báo của bạn về mã hóa, được giải thích trong [hướng dẫn phát hành với EAS](https://devoria.fr/vi/blog/eas-submit-ios-dua-app-expo-len-app-store).

## Check-list của mình trước mỗi lần gửi

- `ascAppId` có trong profile `submit`, được thêm trước khi build.
- Khóa API do EAS quản lý, hoặc ba trường của nó trỏ tới một file `.p8` để ngoài repo.
- Account Holder đã chấp nhận các hợp đồng mới nhất trong App Store Connect.
- `autoIncrement` đang bật, cùng với `"appVersionSource": "remote"`, [được Expo khuyên dùng](https://docs.expo.dev/build-reference/app-versions/) từ EAS CLI 12.
- `version` lớn hơn version được duyệt gần nhất.
- Mỗi API nhạy cảm có trong binary đều có câu giải thích quyền truy cập.
- Với app mới, `appName` và `language` đã được điền trước lần gửi đầu tiên.

## Gửi app đầu tiên mà không bị kẹt

Lệnh này chỉ là một bước. [iOS & Android Kit](https://devoria.fr/vi/kits/ios) xuất phát từ code khởi đầu của các app của mình, có cả subscription, và đi qua bốn bước đến App Store, theo nhịp độ của bạn.

Nếu bạn đang chuẩn bị ra mắt app đầu tiên, [làm bài chẩn đoán miễn phí](https://devoria.fr/vi/diagnostic): nó cho bạn biết điều gì thực sự đang cản bạn và gửi bạn check-list chống bị Apple từ chối của mình. Về trang app, ảnh chụp màn hình và việc duyệt, trang [phát hành ứng dụng lên App Store](https://devoria.fr/vi/expertise/dang-ung-dung-len-app-store) đi lại từng bước.

## Câu hỏi thường gặp

### Tại sao eas submit chỉ thất bại trong CI?

Ở chế độ `--non-interactive`, lệnh không thể hỏi bạn gì cả. Nó cần `ascAppId` trong profile và một khóa API đã gắn với app, hoặc ba trường của khóa trong `eas.json`. Trên máy bạn, nó sẽ hỏi bạn những điều này.

### Có cần build lại sau một lỗi eas submit không?

Chỉ khi Apple từ chối binary, kèm mã ITMS. Ngoại lệ: với ITMS-90054 và 90055, bản build nhắm sai trang app, chỉ cần gửi lại sang đúng trang. Lỗi cấu hình và lỗi tài khoản được sửa trong `eas.json`, trong App Store Connect hoặc trong tài khoản Apple của bạn. Sau đó bạn gửi lại chính bản build đó, ví dụ với `--latest` hoặc `--id`.

### Xem log của một lần gửi EAS Submit ở đâu?

Trên trang của lần submit, trong dashboard của Expo. Expo khuyên đọc log ở đó và khung “Build Annotations”. Trong terminal, thêm `--verbose` để luôn hiển thị log.

### Có dùng được eas submit với tài khoản Apple miễn phí không?

Không. Expo yêu cầu tài khoản Apple Developer trả phí để gửi app lên App Store. Tài khoản miễn phí cho phép thử trên iPhone của chính bạn, không cho dùng App Store Connect.

### Nên cấp vai trò nào cho khóa API dùng với eas submit?

Hướng dẫn của Expo cấp vai trò Admin cho khóa. Theo bảng vai trò của Apple, việc gửi build cũng mở cho vai trò App Manager và Developer. Các bản build Expo của mình chạy với một khóa team có vai trò App Manager. Chỉ Account Holder và Admin mới tạo được khóa team.

### “Increment Build Number” trong EAS nghĩa là gì?

Apple đã từ chối bản build với mã ITMS-90189: số build này đã tồn tại cho version này. Bật `"autoIncrement": true` trong `eas.json`, hoặc tăng `ios.buildNumber`, rồi build lại.


## Dự án của bạn

[Review Kit](https://devoria.fr/vi/kits/app-review): Checklist duyệt App Store của mình, các lần Apple từ chối đã sửa, checklist Google Play và ba skill Claude Code. 1.399.000 ₫.

[iOS & Android Kit](https://devoria.fr/vi/kits/ios): Tạo app iPhone và Android với Claude Code: code khởi đầu từ các app của mình, lộ trình đến tận store và tất cả các kit. 2.799.000 ₫.

[Chẩn đoán: điều gì ngăn bạn có app iPhone](https://devoria.fr/vi/diagnostic): 7 câu hỏi, 60 giây: bạn biết điều gì thật sự cản bạn đăng app iPhone, lộ trình 4 bước và kit phù hợp với bạn.
