# Xử lý lỗi

> Tra cứu cấu trúc response và mã lỗi để biết nên sửa request hay chờ rồi thử lại.

Canonical: https://transcript1.com/vi/docs/errors
Language: vi
Last updated: 2026-11-05

Cần phân biệt hai trường hợp: API từ chối ngay request, hoặc đã nhận request nhưng job gặp lỗi trong lúc xử lý. Trường hợp sau có `status` là `failed`.

## Đọc response lỗi

Với các endpoint `/v1`, lỗi request luôn có cấu trúc sau:

```json
{
  "error": {
    "code": "insufficient_minutes",
    "message": "Not enough minutes",
    "retryable": false,
    "action_url": "https://transcript1.com/dashboard/billing",
    "minutes_needed": 20,
    "minutes_remaining": 3
  }
}
```

Dựa vào `code` để viết logic xử lý, không dựa vào câu chữ trong `message`. Bạn có thể hiển thị `message` cho người dùng, nhưng nội dung này có thể thay đổi. Giá trị `retryable: true` cho biết request có thể thành công nếu thử lại sau.

Tùy mã lỗi, object còn có các trường sau bên cạnh `retryable`:

| Trường | Mã lỗi tương ứng | Cách đọc |
|---|---|---|
| `action_url` | `insufficient_minutes`, `no_active_plan`, `topup_needs_user` | Link mua gói hoặc nạp thêm phút. |
| `minutes_needed`, `minutes_remaining` | `insufficient_minutes` | Số phút cần cho request và số phút bạn còn dùng được. `minutes_needed` là `null` nếu chưa xác định được thời lượng. |
| `reason`, `packs` | `topup_needs_user` | Lý do thẻ không bị trừ tiền và số gói nạp đã yêu cầu. |
| `supported_platforms` | `unsupported_platform` | Danh sách giá trị hợp lệ: `["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"]`. |
| `missing_chunks` | `upload_incomplete` | Index các chunk server chưa nhận được. |
| `max_bytes` | `media_too_large` | Dung lượng file tối đa tài khoản được gửi, tính bằng byte. |
| `params` | `invalid_request` | Khi body hoặc query không hợp lệ, object này liệt kê từng trường và lỗi cần sửa. |

## Request bị từ chối

| Mã HTTP | `code` | Có thể thử lại | Nguyên nhân và cách xử lý |
|---|---|---|---|
| 400, 422 | `invalid_request` | Không | Tham số hoặc trường trong body bị thiếu hay không hợp lệ. Ví dụ: dùng `diarize` cùng `source: "captions"`, dùng `reuse_recent` cùng `webhook_url`, hoặc gửi body JSON sai cú pháp. |
| 422 | `invalid_url` | Không | Link không đúng dạng hỗ trợ, hoặc `platform` không khớp với link. Dùng YouTube, X, TikTok, Instagram, Facebook, Bluesky hay nền tảng khác trong [danh sách nguồn](https://transcript1.com/vi/docs/sources-and-options). |
| 422 | `unsupported_platform` | Không | `platform` không được hỗ trợ. |
| 422 | `unsupported_media` | Không | Đuôi file tải lên không nằm trong danh sách hỗ trợ. |
| 401 | `unauthorized` | Không | Thiếu khóa hoặc token xác thực, hoặc khóa/token không hợp lệ, đã hết hạn hay bị thu hồi. Token tải lên sai hoặc hết hạn cũng nhận mã này. |
| 403 | `insufficient_scope` | Không | Token OAuth thiếu quyền `transcriptions:read` hoặc `transcriptions:write` cần cho thao tác. |
| 403 | `email_not_verified` | Không | Cần xác minh email rồi mới tạo job hoặc phiên trực tiếp. |
| 402 | `insufficient_minutes` | Không | Bạn còn 0 phút, hoặc số phút dùng được không đủ cho job, hay thấp hơn một phút đã bắt đầu của phiên trực tiếp. Nếu [tự động nạp](https://transcript1.com/vi/docs/billing-and-limits#auto-top-up) đang bật, transcript1 sẽ thử nạp trước. Mua phút qua `action_url`, hoặc để AI agent mua bằng `POST /v1/topups` hay tool MCP `top_up`. |
| 402 | `no_active_plan` | Không | Bạn nạp thêm phút khi chưa có gói trả phí còn hiệu lực. Mã này đến từ `POST /v1/topups` và các endpoint thanh toán của bảng điều khiển, không phải endpoint tạo transcript. |
| 402 | `topup_needs_user` | Chỉ với `pending` và `in_progress` | `POST /v1/topups` không trừ tiền thẻ. `reason` cho biết lý do: `not_allowed` (thông tin xác thực không được mua phút nạp, hoặc người dùng sở hữu nó không phải admin của nhóm), `cap_reached` (hạn mức hằng tháng cho AI agent không còn chỗ), `card_declined`, `authentication_required` hoặc `no_card`. Người dùng có thể mua gói nạp qua `action_url`. Với `pending` (thanh toán chưa được xác nhận) và `in_progress` (một lần nạp khác đang chạy), khoản thanh toán vẫn có thể hoàn tất: thử lại sau với cùng `Idempotency-Key` thay vì bảo người dùng đi mua. |
| 404 | `not_found` | Không | Không tìm thấy job, hoặc job thuộc team khác. |
| 409 | `upload_incomplete` | Không | Chưa gửi đủ chunk. Gửi phần còn thiếu rồi gọi hoàn tất lần nữa. |
| 409 | `invalid_state` | Không | Job không còn ở trạng thái chờ tải lên. Không thể gửi chunk hay gọi hoàn tất nữa. |
| 409 | `session_ended` | Không | Phiên trực tiếp đã kết thúc nên `POST /v1/live/sessions/:id/token` không thể cấp token. Cũng được gửi dưới dạng sự kiện `error` của WebSocket. |
| 422 | `unsupported_language` | Không | `language` của phiên trực tiếp không được hỗ trợ. |
| 422 | `unsupported_translation` | Không | Ngôn ngữ `translate_to` của phiên trực tiếp không được hỗ trợ, hoặc dịch trực tiếp không khả dụng. |
| 409 | `idempotency_conflict` | Không | Bạn đã dùng `Idempotency-Key` này cho bộ tham số khác. |
| 413 | `media_too_large` | Không | Dung lượng file vượt giới hạn của tài khoản. Xem [Tải file lên](https://transcript1.com/vi/docs/uploads). |
| 413 | `invalid_request` | Không | Body JSON vượt 1 MB. Với file quá lớn, mã lỗi là `media_too_large`. |
| 429 | `rate_limited` | Có | Vượt số request mỗi phút. Chờ số giây ghi trong `Retry-After`. |
| 429 | `too_many_active_jobs` | Có | Đã chạm giới hạn job đang chạy hoặc phiên tải lên chưa hoàn tất. Chờ theo `Retry-After`. |
| 429 | `too_many_live_sessions` | Có | Số phiên trực tiếp cùng lúc của gói bạn đã dùng hết. Chờ số giây ghi trong `Retry-After` (30). |
| 500 | `internal_error` | Có | Lỗi phía transcript1. Chờ một lát rồi thử lại. |
| 503 | `service_unavailable` | Có | transcript1 tạm thời chưa phục vụ được request. Chờ một lát rồi thử lại. |
| 503 | `live_capacity` | Có | Toàn bộ dung lượng trực tiếp đang được dùng. Chờ số giây ghi trong `Retry-After` (30) rồi tạo lại phiên. |
| 503 | `live_unavailable` | Không | Chế độ trực tiếp hiện không khả dụng. |

Nếu trình duyệt xác thực bằng cookie đăng nhập thay cho bearer token, API còn kiểm tra origin. Request từ website khác sẽ nhận `403 forbidden_origin`. Cơ chế này không áp dụng cho lệnh gọi API có header `Authorization`.

Response `429` luôn có header `Retry-After`, tính bằng giây. Xem các hạn mức cụ thể trong [Thanh toán và giới hạn](https://transcript1.com/vi/docs/billing-and-limits).

## Khi nào nên gửi lại request?

- Chỉ tự thử lại nếu `retryable` là `true`: `rate_limited`, `too_many_active_jobs`, `too_many_live_sessions`, `live_capacity`, `internal_error`, `service_unavailable`, hoặc `topup_needs_user` có `reason` là `pending` hay `in_progress`. Có `Retry-After` thì chờ đúng thời gian đó; không có thì tăng dần thời gian chờ giữa các lần.
- Gửi lại `POST /v1/transcriptions` hoặc `POST /v1/topups` với nguyên `Idempotency-Key` đã dùng. Nếu request đầu đã thành công, cách này tránh tạo thêm lần tính phí.
- Gặp mã khác thì sửa request trước, không gửi lại nguyên trạng.

## Chuyển thành văn bản trực tiếp

`POST /v1/live/sessions` và các endpoint trực tiếp khác dùng cùng cấu trúc lỗi. `invalid_request`, `unsupported_language`, `insufficient_minutes` và `email_not_verified` cũng áp dụng cho chúng, cùng với các mã trực tiếp trong bảng ở trên. Việc tạo phiên không idempotent, nên thử lại sau `too_many_live_sessions` hoặc `live_capacity` sẽ tạo một phiên mới.

Sau khi phiên đã tồn tại, sự cố đến qua WebSocket dưới dạng sự kiện `error` và mã đóng, không phải lỗi HTTP. Xem [Chuyển thành văn bản trực tiếp](https://transcript1.com/vi/docs/live#websocket-protocol) để biết mọi sự kiện.

| Mã đóng | Ý nghĩa |
|---|---|
| `1000` | Đóng bình thường, sau `session.ended`. |
| `1011` | Lỗi server ngoài dự kiến. Hãy tiếp tục bằng token mới. |
| `1012` | Server khởi động lại, sau `reconnect`. Hãy tiếp tục bằng token mới. |
| `4001` | Token không hợp lệ, đã hết hạn hoặc đã được dùng. |
| `4004` | Không tìm thấy phiên hoặc phiên đã kết thúc. |
| `4008` | Vi phạm giao thức: frame âm thanh có độ dài lẻ hoặc quá lớn. |
| `4010` | Bị thay thế bởi một kết nối mới hơn vào cùng phiên. |

Phiên trực tiếp kết thúc vì lý do như `balance_exhausted` hay `idle_timeout` không phải là lỗi: nó gửi `session.ended` kèm lý do kết thúc và được lưu. Xem [Lý do kết thúc](https://transcript1.com/vi/docs/live#end-reasons).

## Job lỗi trong lúc xử lý

Job có thể gặp lỗi sau khi API đã trả response. Khi `status` là `failed`, object transcription chứa `error: { "code", "message" }`. Kiểm tra `error` mỗi khi poll trạng thái hoặc nhận [webhook](https://transcript1.com/vi/docs/webhooks) `transcription.failed`. Nếu bản dịch thất bại, webhook `transcription.translation.failed` được gửi đi và bản chép lời không bao giờ bị lỗi vì việc đó.

| `error.code` | Nguyên nhân | Nên làm gì? |
|---|---|---|
| `media_unavailable` | Video không tồn tại hoặc đã không còn truy cập được. | Không thử lại nguyên link; kiểm tra URL. |
| `media_private` | Video đang để riêng tư. | Dùng bài đăng công khai, hoặc video YouTube công khai hay không công khai (unlisted). |
| `login_required` | Phải đăng nhập mới xem được nội dung. | Không thử lại. Đây là lỗi cố định, hệ thống không tự thử lại. |
| `no_audio` | Video không có track audio. | Không thử lại. Đây là lỗi cố định, hệ thống không tự thử lại. |
| `age_restricted` | Video có giới hạn độ tuổi. | Không thử lại bằng link; tải file lên thay thế. |
| `live_stream` | Buổi phát đang trực tiếp hoặc chưa bắt đầu. | Chờ buổi phát kết thúc rồi thử lại. |
| `no_captions` | Bạn chọn `source` là `captions` nhưng video không có track phụ đề phù hợp. | Đổi sang `source: "auto"` hoặc `"stt"`. |
| `media_too_long` | Bản ghi vượt giới hạn 5 giờ của speech-to-text. | Chia nhỏ bản ghi trước khi gửi lại. |
| `unsupported_media` | File không đọc được hoặc thiếu track audio. | Gửi lại bằng định dạng file được hỗ trợ. |
| `no_speech` | Không phát hiện giọng nói. | Không thử lại. |
| `unsupported_language` | Bản ghi dùng ngôn ngữ chưa được hỗ trợ. | Không thử lại nguyên request; đặt `language` thành mã được hỗ trợ hoặc `"auto"`. |
| `insufficient_minutes` | Đến lúc tính phí cho job, tài khoản không còn đủ phút và tự động nạp, nếu đang bật, cũng không thêm được phút. | Nạp thêm phút rồi thử lại. |
| `fetch_failed` | Hệ thống đã thử nhiều lần nhưng vẫn không tải được audio hoặc video. | Có thể thử lại sau. |
| `transcription_failed` | Đã thử nhiều lần nhưng speech-to-text vẫn gặp lỗi. | Có thể thử lại sau. |

Nếu job thất bại hoặc bạn hủy, bạn không mất phí. Phút đã trừ cho job đó sẽ được hoàn lại.
