Files
goodgo-platform/docs/api-error-codes.md
Ho Ngoc Hai 11f2bf26e6
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled
chore: update project documentation, audit reports, and initialize IDE configuration files
2026-04-19 03:12:54 +07:00

16 KiB

Tài Liệu Tham Khảo Mã Lỗi API

Tất cả các lỗi của GoodGo Platform API đều tuân theo định dạng JSON có cấu trúc thống nhất. Tài liệu này liệt kê mọi giá trị errorCode, mã HTTP tương ứng, mô tả và ví dụ sử dụng.

Định Dạng Phản Hồi Lỗi

Mỗi phản hồi lỗi từ API có dạng như sau:

{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Human-readable error description",
  "details": { },
  "correlationId": "optional-uuid",
  "timestamp": "2026-04-10T12:00:00.000Z"
}
Field Type Description
statusCode number Mã HTTP status (ví dụ: 400, 401, 404).
errorCode string Mã lỗi dạng máy đọc được (xem các bảng bên dưới).
message string Giải thích dạng người đọc được (tiếng Việt cho các quy tắc nghiệp vụ, tiếng Anh cho lỗi hệ thống).
details object Metadata có cấu trúc tùy chọn (ví dụ: tên trường validation, trạng thái hiện tại/mục tiêu).
correlationId string Phản chiếu từ header X-Correlation-Id của request khi có.
timestamp string Dấu thời gian ISO 8601 tại thời điểm xảy ra lỗi.

Mã Lỗi Chung

Các mã này áp dụng cho tất cả các module. Chúng cũng được dùng làm mã dự phòng khi một HttpException thông thường (không có mã lỗi domain) được ném ra.

Error Code HTTP Status Description When It Occurs
INTERNAL_ERROR 500 Lỗi máy chủ không mong đợi. Ngoại lệ chưa được xử lý, lỗi hạ tầng.
VALIDATION_FAILED 400 Dữ liệu request không qua xác thực. Trường dữ liệu không hợp lệ, vi phạm quy tắc nghiệp vụ (ví dụ: định dạng số điện thoại sai, chuyển trạng thái không hợp lệ).
NOT_FOUND 404 Tài nguyên được yêu cầu không tồn tại. Tra cứu thực thể theo ID không có kết quả.
CONFLICT 409 Xung đột trạng thái tài nguyên. Tạo trùng lặp, chuyển đổi trạng thái xung đột.
UNAUTHORIZED 401 Yêu cầu xác thực hoặc xác thực thất bại. Token xác thực thiếu/không hợp lệ/hết hạn, thông tin đăng nhập sai.
FORBIDDEN 403 Không đủ quyền truy cập. Từ chối quyền theo vai trò, lỗi CSRF token, không khớp quyền sở hữu tài nguyên.
BAD_REQUEST 400 Request không đúng định dạng. Tải lên tệp không hợp lệ, loại nội dung không được hỗ trợ, tham số truy vấn sai.
TOO_MANY_REQUESTS 429 Vượt quá giới hạn tốc độ. Gửi quá nhiều request API trong thời gian ngắn.

Mã Lỗi Xác Thực

Error Code HTTP Status Description When It Occurs
AUTH_INVALID_CREDENTIALS 401 Thông tin đăng nhập không chính xác. Số điện thoại hoặc mật khẩu không khớp khi đăng nhập nội bộ.
AUTH_TOKEN_EXPIRED 401 JWT token đã hết hạn. Access token hoặc refresh token đã quá thời gian hết hạn.
AUTH_TOKEN_INVALID 401 JWT token bị sai định dạng hoặc bị giả mạo. Xác minh chữ ký token thất bại.
AUTH_INSUFFICIENT_PERMISSIONS 403 Người dùng đã xác thực không có vai trò/quyền cần thiết. Người dùng không phải admin truy cập endpoint admin, không khớp vai trò.

Ví Dụ Sử Dụng Module Xác Thực

// POST /auth/login — wrong password
{
  "statusCode": 401,
  "errorCode": "UNAUTHORIZED",
  "message": "Số điện thoại hoặc mật khẩu không đúng"
}

// POST /auth/refresh — expired refresh token
{
  "statusCode": 401,
  "errorCode": "UNAUTHORIZED",
  "message": "Refresh token không hợp lệ hoặc đã hết hạn"
}

// POST /auth/register — phone already registered
{
  "statusCode": 409,
  "errorCode": "CONFLICT",
  "message": "Số điện thoại đã được đăng ký"
}

// POST /auth/register — invalid phone format
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Số điện thoại không hợp lệ. Yêu cầu 10 chữ số bắt đầu bằng 0"
}

Mã Lỗi Người Dùng

Error Code HTTP Status Description When It Occurs
USER_NOT_FOUND 404 Người dùng không tồn tại. Tra cứu hồ sơ, quản lý người dùng qua admin.
USER_ALREADY_EXISTS 409 Người dùng với định danh đã cho tồn tại. Đăng ký trùng lặp (số điện thoại hoặc email).
USER_INVALID_PHONE 400 Định dạng số điện thoại không hợp lệ. Đăng ký hoặc cập nhật hồ sơ với số điện thoại sai định dạng.

Ví Dụ Sử Dụng Module Người Dùng

// GET /auth/profile — deleted or nonexistent user
{
  "statusCode": 404,
  "errorCode": "NOT_FOUND",
  "message": "Người dùng with id '...' not found"
}

// DELETE /auth/account — account already deleted
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Tài khoản đã bị xóa"
}

Mã Lỗi Tin Đăng

Error Code HTTP Status Description When It Occurs
LISTING_NOT_FOUND 404 Tin đăng không tồn tại. Tra cứu theo ID để xem, kiểm duyệt, hoặc thay đổi trạng thái.
LISTING_INVALID_STATUS_TRANSITION 400 Chuyển đổi trạng thái không được phép. Thực hiện bước quy trình không hợp lệ (ví dụ: EXPIREDACTIVE).
LISTING_ALREADY_ACTIVE 409 Tin đăng đã ở trạng thái hoạt động. Kích hoạt lại tin đăng đã được xuất bản.
LISTING_EXPIRED 400 Tin đăng đã hết hạn. Thực hiện các thao tác trên tin đăng đã hết hạn.

Ví Dụ Sử Dụng Module Tin Đăng

// PATCH /listings/:id/status — invalid transition
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Không thể chuyển trạng thái từ EXPIRED sang ACTIVE",
  "details": {
    "currentStatus": "EXPIRED",
    "targetStatus": "ACTIVE"
  }
}

// POST /admin/listings/:id/approve — already approved
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Listing này không ở trạng thái chờ duyệt"
}

// POST /listings — invalid address
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Địa chỉ không hợp lệ"
}

Mã Lỗi Bất Động Sản

Error Code HTTP Status Description When It Occurs
PROPERTY_NOT_FOUND 404 Bất động sản không tồn tại. Tra cứu bất động sản để tải media hoặc tạo tin đăng.

Ví Dụ Sử Dụng Module Bất Động Sản

// POST /properties/:id/media — property not found
{
  "statusCode": 404,
  "errorCode": "NOT_FOUND",
  "message": "Property with id '...' not found"
}

// POST /properties/:id/media — too many files
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Tối đa 30 ảnh/video cho mỗi bất động sản"
}

Mã Lỗi Media

Error Code HTTP Status Description When It Occurs
MEDIA_UPLOAD_FAILED 500 Tải tệp lên nhà cung cấp lưu trữ thất bại. Lỗi ghi vào S3/cloud storage.
MEDIA_LIMIT_EXCEEDED 400 Đã đạt số lượng tệp media tối đa. Vượt quá giới hạn media cho mỗi bất động sản.

Mã Lỗi Thanh Toán

Error Code HTTP Status Description When It Occurs
PAYMENT_FAILED 500 Xử lý thanh toán thất bại. Lỗi cổng thanh toán, hết thời gian chờ nhà cung cấp, lỗi tạo liên kết thanh toán.
PAYMENT_ALREADY_PROCESSED 409 Thanh toán đã được hoàn tất, thất bại, hoặc hoàn tiền. Callback trùng lặp, thử lại trên thanh toán không ở trạng thái chờ, hoàn tiền thanh toán chưa hoàn tất.
PAYMENT_INVALID_AMOUNT 400 Số tiền thanh toán không hợp lệ. Số tiền bằng 0/âm, số tiền dưới ngưỡng tối thiểu.

Ví Dụ Sử Dụng Module Thanh Toán

// POST /payments — duplicate idempotency key
{
  "statusCode": 409,
  "errorCode": "CONFLICT",
  "message": "Thanh toán với idempotency key này đã tồn tại"
}

// POST /payments/callback — invalid signature
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Chữ ký callback không hợp lệ"
}

// POST /payments/:id/refund — payment not completed
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Chỉ có thể hoàn tiền cho thanh toán đã hoàn tất"
}

// Entity-level: marking already-processed payment as completed
{
  "statusCode": 409,
  "errorCode": "PAYMENT_ALREADY_PROCESSED",
  "message": "Cannot complete payment in status COMPLETED"
}

Mã Lỗi Gói Dịch Vụ

Error Code HTTP Status Description When It Occurs
SUBSCRIPTION_NOT_FOUND 404 Gói dịch vụ không tồn tại. Tra cứu để hủy, nâng cấp, hoặc đo lường sử dụng.
SUBSCRIPTION_ALREADY_ACTIVE 409 Người dùng đã có gói dịch vụ đang hoạt động. Cố tạo gói dịch vụ hoạt động thứ hai.
SUBSCRIPTION_ALREADY_CANCELLED 409 Gói dịch vụ đã bị hủy trước đó. Hủy lại gói dịch vụ đã hủy.
SUBSCRIPTION_INACTIVE 409 Gói dịch vụ không ở trạng thái hoạt động. Nâng cấp, hết hạn, hoặc đánh dấu quá hạn trên gói không hoạt động.
QUOTA_EXCEEDED 403 Đã vượt quá hạn mức sử dụng của gói hiện tại. Vượt quá giới hạn tin đăng, khách hàng tiềm năng, hoặc tìm kiếm.

Ví Dụ Sử Dụng Module Gói Dịch Vụ

// POST /subscriptions — already has active subscription
{
  "statusCode": 409,
  "errorCode": "CONFLICT",
  "message": "Người dùng đã có subscription đang hoạt động"
}

// POST /subscriptions/cancel — already cancelled
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Subscription đã bị hủy trước đó"
}

// POST /subscriptions/upgrade — subscription not active
{
  "statusCode": 400,
  "errorCode": "VALIDATION_FAILED",
  "message": "Subscription không ở trạng thái hoạt động"
}

// Entity-level: upgrade on inactive subscription
{
  "statusCode": 409,
  "errorCode": "SUBSCRIPTION_INACTIVE",
  "message": "Không thể nâng cấp subscription ở trạng thái CANCELLED"
}

// Quota guard — plan limit exceeded
{
  "statusCode": 403,
  "errorCode": "FORBIDDEN",
  "message": "Bạn đã đạt giới hạn listings. Vui lòng nâng cấp gói để tiếp tục."
}

Mã Lỗi Khóa Học

Error Code HTTP Status Description When It Occurs
COURSE_NOT_FOUND 404 Khóa học không tồn tại. Tra cứu khóa học theo ID.
COURSE_ALREADY_PUBLISHED 409 Khóa học đã ở trạng thái xuất bản. Xuất bản lại khóa học đang hoạt động.
COURSE_ENROLLMENT_CLOSED 400 Thời hạn đăng ký khóa học đã kết thúc. Cố đăng ký sau ngày chốt danh sách.

Tra Cứu Nhanh Mã Lỗi (Sắp Xếp Theo Bảng Chữ Cái)

Error Code HTTP Status Module
AUTH_INSUFFICIENT_PERMISSIONS 403 Auth
AUTH_INVALID_CREDENTIALS 401 Auth
AUTH_TOKEN_EXPIRED 401 Auth
AUTH_TOKEN_INVALID 401 Auth
BAD_REQUEST 400 General
CONFLICT 409 General
COURSE_ALREADY_PUBLISHED 409 Course
COURSE_ENROLLMENT_CLOSED 400 Course
COURSE_NOT_FOUND 404 Course
FORBIDDEN 403 General
INTERNAL_ERROR 500 General
LISTING_ALREADY_ACTIVE 409 Listing
LISTING_EXPIRED 400 Listing
LISTING_INVALID_STATUS_TRANSITION 400 Listing
LISTING_NOT_FOUND 404 Listing
MEDIA_LIMIT_EXCEEDED 400 Media
MEDIA_UPLOAD_FAILED 500 Media
NOT_FOUND 404 General
PAYMENT_ALREADY_PROCESSED 409 Payment
PAYMENT_FAILED 500 Payment
PAYMENT_INVALID_AMOUNT 400 Payment
PROPERTY_NOT_FOUND 404 Property
QUOTA_EXCEEDED 403 Subscription
SUBSCRIPTION_ALREADY_ACTIVE 409 Subscription
SUBSCRIPTION_ALREADY_CANCELLED 409 Subscription
SUBSCRIPTION_INACTIVE 409 Subscription
SUBSCRIPTION_NOT_FOUND 404 Subscription
TOO_MANY_REQUESTS 429 General
UNAUTHORIZED 401 General
USER_ALREADY_EXISTS 409 User
USER_INVALID_PHONE 400 User
USER_NOT_FOUND 404 User

Hướng Dẫn Tích Hợp Frontend

Kiểu Lỗi TypeScript

interface ApiError {
  statusCode: number;
  errorCode: string;
  message: string;
  details?: Record<string, unknown>;
  correlationId?: string;
  timestamp: string;
}

Xử Lý Lỗi Theo Mã

import axios, { AxiosError } from 'axios';

async function handleApiCall() {
  try {
    const res = await axios.post('/api/listings', payload);
    return res.data;
  } catch (err) {
    if (axios.isAxiosError(err)) {
      const apiError = err.response?.data as ApiError;

      switch (apiError.errorCode) {
        case 'VALIDATION_FAILED':
          // Show field-level errors from apiError.details
          showValidationErrors(apiError.details);
          break;
        case 'UNAUTHORIZED':
        case 'AUTH_TOKEN_EXPIRED':
          // Redirect to login or refresh token
          await refreshSession();
          break;
        case 'QUOTA_EXCEEDED':
          // Show upgrade prompt
          showUpgradeDialog(apiError.message);
          break;
        case 'CONFLICT':
          // Inform user of duplicate action
          showToast(apiError.message, 'warning');
          break;
        case 'NOT_FOUND':
          // Navigate to 404 page or show missing resource message
          router.push('/404');
          break;
        default:
          showToast('Đã xảy ra lỗi, vui lòng thử lại', 'error');
      }
    }
  }
}

Hằng Số Mã Lỗi (Tùy Chọn)

Để đảm bảo an toàn kiểu, hãy duy trì các mã lỗi dưới dạng union type ở phía frontend:

type ErrorCode =
  | 'INTERNAL_ERROR'
  | 'VALIDATION_FAILED'
  | 'NOT_FOUND'
  | 'CONFLICT'
  | 'UNAUTHORIZED'
  | 'FORBIDDEN'
  | 'BAD_REQUEST'
  | 'TOO_MANY_REQUESTS'
  | 'AUTH_INVALID_CREDENTIALS'
  | 'AUTH_TOKEN_EXPIRED'
  | 'AUTH_TOKEN_INVALID'
  | 'AUTH_INSUFFICIENT_PERMISSIONS'
  | 'USER_NOT_FOUND'
  | 'USER_ALREADY_EXISTS'
  | 'USER_INVALID_PHONE'
  | 'COURSE_NOT_FOUND'
  | 'COURSE_ALREADY_PUBLISHED'
  | 'COURSE_ENROLLMENT_CLOSED'
  | 'LISTING_NOT_FOUND'
  | 'LISTING_INVALID_STATUS_TRANSITION'
  | 'LISTING_ALREADY_ACTIVE'
  | 'LISTING_EXPIRED'
  | 'PROPERTY_NOT_FOUND'
  | 'MEDIA_UPLOAD_FAILED'
  | 'MEDIA_LIMIT_EXCEEDED'
  | 'PAYMENT_FAILED'
  | 'PAYMENT_ALREADY_PROCESSED'
  | 'PAYMENT_INVALID_AMOUNT'
  | 'SUBSCRIPTION_NOT_FOUND'
  | 'SUBSCRIPTION_ALREADY_ACTIVE'
  | 'SUBSCRIPTION_ALREADY_CANCELLED'
  | 'SUBSCRIPTION_INACTIVE'
  | 'QUOTA_EXCEEDED';

Tệp Nguồn

  • Enum mã lỗi: apps/api/src/modules/shared/domain/error-codes.ts
  • Domain exceptions: apps/api/src/modules/shared/domain/domain-exception.ts
  • Global exception filter: apps/api/src/modules/shared/infrastructure/filters/global-exception.filter.ts