# Lỗi

Mọi lỗi của `/api/open/v1` có dạng **RFC 9457** (`application/problem+json`) kèm trường `code` ổn định:

```json
{
  "type": "https://danix.vn/developers/errors#rate-limited",
  "title": "Vượt hạn mức gọi API, hãy chờ rồi thử lại",
  "status": 429,
  "code": "rate-limited",
  "detail": "Chờ 12 giây rồi thử lại."
}
```

| Trường | Ý nghĩa |
| --- | --- |
| `type` | Địa chỉ giải thích loại lỗi; phần neo `#…` chính là `code`. |
| `title` | Tên ngắn của loại lỗi, ổn định theo `code`. |
| `status` | Mã trạng thái HTTP. |
| `code` | Mã máy đọc, chữ thường nối bằng gạch ngang. **Dùng trường này để rẽ nhánh xử lý**, không dùng `title` hay `detail`. |
| `detail` | Mô tả cho đúng lượt lỗi này, dành cho người đọc; câu chữ có thể đổi. |
| `errors` | Chỉ có ở `validation-failed`, khi lỗi gắn với trường cụ thể (thân, query hay tham số đường dẫn sai hình dạng): danh sách trường sai, mỗi phần tử gồm `path` (đường tới trường, ví dụ `lines.0.quantity`) và `message` (vì sao sai). Từ chối nghiệp vụ không gắn với một trường (ví dụ mẫu mã không tồn tại trong shop) chỉ có `detail`. |

Client cần bỏ qua trường lạ trong thân lỗi, và coi `code` lạ như lỗi cùng nhóm với `status` của nó. Danh sách `code` có thể được bổ sung mà không đổi phiên bản.

## Cách xử lý theo nhóm

- **400, 413, 415 và 422**: request sai, thử lại y nguyên sẽ sai tiếp. Sửa request.
- **401 và 403**: vấn đề khoá hay quyền, không phải mạng. Đừng thử lại dồn dập; kiểm tra khoá và quyền.
- **402**: gói của shop hết hạn; chờ shop gia hạn.
- **404**: không có bản ghi, hoặc bản ghi thuộc shop khác, hoặc trang chat ngoài danh sách của khoá. Cố ý không phân biệt ba trường hợp này.
- **409**: xung đột trạng thái hoặc lượt trước còn chạy. Một số mã dặn thử lại sau một lúc.
- **429**: chờ theo `Retry-After`; xem [Hạn mức gọi](/developers/rate-limits).
- **5xx**: lỗi phía DANIX, hãng vận chuyển (503 `carrier-unavailable`) hoặc kênh chat (503 `channel-unavailable`). Thử lại với khoảng chờ tăng dần; dùng [Idempotency-Key](/developers/idempotency) để thử lại không tạo trùng.

## Bảng mã lỗi

### validation-failed {#validation-failed}

**400.** Thân, tham số query hoặc tham số đường dẫn không hợp lệ, hoặc dữ liệu hợp lệ về hình dạng nhưng không dùng được (ví dụ mã tham chiếu không có trong shop). Khi lỗi gắn với trường cụ thể, mảng `errors` chỉ rõ từng trường (`path` và `message`); từ chối không gắn với một trường chỉ có `detail`. Sửa theo đó rồi gửi lại.

### invalid-cursor {#invalid-cursor}

**400.** `cursor` sai hình dạng hoặc không phải giá trị API đã trả. Bắt đầu lại từ trang đầu, hoặc dùng đúng `nextCursor` của trang trước. Xem [Phân trang và đồng bộ](/developers/pagination).

### api-key-not-accepted {#api-key-not-accepted}

**400.** Bạn gửi khoá API tới đường đăng xuất hoặc đường đăng ký thông báo đẩy, vốn chỉ dành cho phiên đăng nhập của người dùng. Các đường khác ngoài `/api/open/v1` nhận 401 như khi chưa đăng nhập. Khoá API chỉ dùng cho `/api/open/v1`.

### invalid-api-key {#invalid-api-key}

**401.** Thiếu header `Authorization`, khoá sai định dạng, không tồn tại, đã thu hồi hoặc đã hết hạn. Kiểm tra khoá; nếu vừa xoay, dùng khoá mới. Không thử lại cùng khoá.

### subscription-expired {#subscription-expired}

**402.** Gói của shop đã hết hạn nên shop ở chế độ chỉ đọc; chỉ request ghi bị từ chối, request `GET` vẫn chạy. Shop gia hạn gói là hết lỗi.

### insufficient-permission {#insufficient-permission}

**403.** Khoá không có quyền cho đường này, hoặc thân yêu cầu dùng một trường cần quyền riêng (ví dụ đặt `unitPrice` khác giá niêm yết, giảm giá hay ghi `payments` khi tạo đơn). Quyền cần có — và "Quyền thêm theo trường" — ghi ở từng endpoint trong phần tham chiếu. Nhờ quản trị viên shop bổ sung quyền cho ứng dụng kết nối.

### shop-suspended {#shop-suspended}

**403.** Shop đang bị đình chỉ. Mọi request bị từ chối cho tới khi shop được mở lại.

### automation-not-active {#automation-not-active}

**403.** Gói của shop không có tính năng Tự động hoá nên khoá API không dùng được. Shop cần nâng gói.

### not-found {#not-found}

**404.** Không có bản ghi hoặc đường dẫn này với khoá của bạn. Cũng là kết quả khi bản ghi thuộc shop khác hoặc trang chat nằm ngoài danh sách được chọn cho khoá.

### conflict {#conflict}

**409.** Thao tác xung đột với trạng thái hiện tại (ví dụ đơn đã ở trạng thái không cho phép bước này). Đọc lại bản ghi rồi quyết định.

### idempotency-key-in-progress {#idempotency-key-in-progress}

**409.** Lượt trước dùng cùng `Idempotency-Key` vẫn đang chạy. Phản hồi trả ngay, kèm `Retry-After: 1`. Chờ rồi gửi lại **y nguyên** request; đừng đổi khoá. Xem [Idempotency-Key](/developers/idempotency).

### idempotency-key-reused {#idempotency-key-reused}

**422.** `Idempotency-Key` này đã dùng cho một request có thân khác. Mỗi khoá chỉ gắn với một nội dung; dùng khoá mới cho nội dung mới.

### payload-too-large {#payload-too-large}

**413.** Thân yêu cầu vượt quá dung lượng máy chủ nhận. Thân của mọi endpoint đều nhỏ (một đơn tối đa 200 dòng hàng); gặp mã này là request đang gửi thứ không cần thiết. Thu gọn thân, đừng thử lại y nguyên.

### unsupported-media-type {#unsupported-media-type}

**415.** Thân yêu cầu không phải JSON. Gửi kèm header `Content-Type: application/json` và thân JSON hợp lệ.

### rate-limited {#rate-limited}

**429.** Vượt hạn mức của khoá. Chờ đủ thời gian trong header `Retry-After` (số giây) rồi thử lại. Xem [Hạn mức gọi](/developers/rate-limits).

### internal-error {#internal-error}

**500.** Lỗi phía DANIX. Thử lại với khoảng chờ tăng dần; nếu lỗi kéo dài, liên hệ hỗ trợ kèm thời điểm và đường dẫn đã gọi.

### carrier-unavailable {#carrier-unavailable}

**503.** Hãng vận chuyển hiện không với tới được (mạng, hãng đang lỗi hoặc kết nối của shop chưa dùng được), nên thao tác tạo hay huỷ vận đơn chưa hoàn tất. Thử lại sau một lúc với khoảng chờ tăng dần; trước khi tạo lại, đọc `GET /shipments?orderId=…` để biết lượt trước đã để lại vận đơn nào chưa. Lỗi kéo dài thì báo quản trị viên shop kiểm tra kết nối hãng.

### channel-unavailable {#channel-unavailable}

**503.** Kênh chat (Facebook, Zalo) tạm thời không phản hồi — kênh đang giới hạn tốc độ gọi của trang, máy chủ của kênh lỗi hoặc mạng chập chờn — nên thao tác gửi tin, nhắn riêng hay ẩn, hiện bình luận chưa hoàn tất. Khác `rate-limited`: đây không phải hạn mức của khoá API. Thử lại sau một lúc với khoảng chờ tăng dần.

### order-has-live-shipment {#order-has-live-shipment}

**409.** Đơn còn vận đơn chưa kết thúc nên không thực hiện được thao tác này (ví dụ sửa nội dung hàng). Huỷ hoặc chờ vận đơn kết thúc, rồi thao tác lại.

### order-status-not-allowed {#order-status-not-allowed}

**422.** Không chuyển được đơn sang trạng thái này từ trạng thái hiện tại. Đọc lại đơn để biết trạng thái hiện hành rồi chọn bước hợp lệ.

### insufficient-stock {#insufficient-stock}

**422.** Không đủ tồn kho cho thao tác này. Kiểm tra tồn kho bằng `GET /inventory/stock`, giảm số lượng hoặc nhập thêm hàng rồi thử lại.

### variant-removed {#variant-removed}

**422.** Một mẫu mã trong request đã bị gỡ khỏi sản phẩm. Lấy lại sản phẩm để biết mẫu mã còn hiệu lực.

### duplicate-code {#duplicate-code}

**409.** Mã bạn đặt (mã đơn, mã sản phẩm…) đã tồn tại trong shop. Chọn mã khác.

### customer-blocked {#customer-blocked}

**422.** Khách hàng đang bị chặn nên không thực hiện được thao tác này.

### document-already-posted {#document-already-posted}

**409.** Phiếu kho đã ghi sổ rồi nên không ghi sổ lại hay sửa được.

### shipment-not-cancellable {#shipment-not-cancellable}

**409.** Vận đơn không còn huỷ được ở trạng thái hiện tại. Đọc `GET /shipments/:id` để xem trạng thái và hành trình.

### messaging-window-closed {#messaging-window-closed}

**422.** Đã quá khung thời gian nền tảng cho phép nhắn cho khách này (ví dụ 24 giờ với Facebook). Chỉ gửi lại được khi khách nhắn tin mới.

### page-not-connected {#page-not-connected}

**422.** Trang chat của hội thoại không còn kết nối nên không gửi được. Quản trị viên shop cần nối lại trang.

### conversation-not-replyable {#conversation-not-replyable}

**422.** Hội thoại này không trả lời được qua API ở trạng thái hiện tại.
