# 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-10-19

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` | 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. |
| `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. |
| 402 | `insufficient_minutes` | Không | Bạn còn 0 phút, hoặc số phút dùng được không đủ cho job. |
| 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ừ endpoint thanh toán của bảng điều khiển, không phải endpoint tạo transcript. |
| 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 | `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`. |
| 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. |

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`, `internal_error` hoặc `service_unavailable`. 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` 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.

## 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`.

| `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. | 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.
