Python

Xử lý lỗi

Hệ thống exception và cách xử lý lỗi trong Python SDK

SDK sử dụng hệ thống exception phân cấp:

Danh sách exception

ExceptionMô tả
SSIErrorBase exception — có message, code, status_code, response_body, headers
AuthenticationErrorXác thực thất bại (sai credentials, token hết hạn)
SmartOTPPendingErrorCon của AuthenticationError. Smart OTP chưa được duyệt trên thiết bị (HTTP 202 / 401114) hoặc hết thời gian chờ polling. Gọi lại ensure_authenticated(transaction_id=...) với cùng transaction_id
SmartOTPRejectedErrorCon của AuthenticationError. Smart OTP bị từ chối, hết hạn hoặc không tìm thấy giao dịch (401113, 401115, 401116). SDK dừng ngay, không polling tiếp
ReauthenticationRequiredCon của AuthenticationError. Không thể refresh token, hoặc server từ chối refresh (401101, 401103). SDK xoá token đang giữ và không retry; cần xác thực lại bằng OTP mới
APIErrorAPI trả về lỗi — code là mã lỗi của server (hoặc HTTP status dạng chuỗi), kèm status_code, response_body
DuplicateRequestErrorCon của APIError. HTTP 409: clientRequestId / batchRequestId đã được dùng trong ngày. SDK không tự gửi lại lệnh với id mới vì lệnh gốc có thể đang hoạt động
WebSocketErrorLỗi kết nối hoặc giao tiếp WebSocket
TradingWSErrorCon của WebSocketError. Frame lỗi từ Trading WebSocket — có status, error_code, server_message. Xem Trading WebSocket
ValidationErrorLỗi validate input (kiểm tra phía SDK, trước khi gửi request)
RateLimitErrorVượt quá giới hạn request — có thêm status_code, retry_after_ms, limit, remaining, reset

Chuỗi kế thừa

Exception → SSIError → AuthenticationError → SmartOTPPendingError
                                           → SmartOTPRejectedError
                                           → ReauthenticationRequired
Exception → SSIError → APIError → DuplicateRequestError
Exception → SSIError → WebSocketError → TradingWSError
Exception → SSIError → ValidationError
Exception → SSIError → RateLimitError

Sử dụng cơ bản

from ssi_sdk import SSIError

try:
    auth.authenticate(otp="<OTP>")
except SSIError as e:
    print(f"Lỗi: {e.message} (code: {e.code})")

Xử lý chi tiết theo loại lỗi

from ssi_sdk.exceptions import (
    APIError,
    AuthenticationError,
    DuplicateRequestError,
    RateLimitError,
)
from ssi_sdk.enums import OrderSide

try:
    result = trading.trading.place_limit_order(
        account_no="<ACCOUNT_NO>",
        symbol="SSI",
        side=OrderSide.BUY,
        quantity=100,
        price=68000,
    )
except AuthenticationError:
    print("Cần xác thực lại")
except RateLimitError as e:
    print(f"Rate limited, retry sau {e.retry_after_ms} ms")
except DuplicateRequestError:
    print("client_request_id đã dùng trong ngày, không gửi lại")
except APIError as e:
    print(f"API error {e.status_code} (code {e.code}): {e.message}")

DuplicateRequestError là con của APIError, nên phải bắt nó trước APIError.

Smart OTP và xác thực lại

from ssi_sdk.exceptions import (
    ReauthenticationRequired,
    SmartOTPPendingError,
    SmartOTPRejectedError,
)

otp = auth.request_otp_typed()          # OTPResponse(message, transaction_id)
try:
    auth.ensure_authenticated(transaction_id=otp.transaction_id)
except SmartOTPPendingError:
    # Chưa duyệt kịp: gọi lại với cùng transaction_id
    auth.ensure_authenticated(transaction_id=otp.transaction_id)
except SmartOTPRejectedError:
    print("Smart OTP bị từ chối hoặc hết hạn, yêu cầu OTP mới")
except ReauthenticationRequired:
    print("Phiên không refresh được, cần xác thực lại bằng OTP mới")
  • Mỗi apiKey chỉ có 1 phiên hoạt động. Khi dùng lại refresh token, server thu hồi mọi phiên của apiKey đó (401103).
  • ReauthenticationRequired không bao giờ được retry tự động.

RateLimitError

  • retry_after_ms là mili-giây (SDK đổi từ header Retry-After tính bằng giây).
  • limit và remaining lấy từ header X-RateLimit-Limit / X-RateLimit-Remaining nếu server gửi.
  • reset thường là None vì server không gửi thời điểm reset.

ServerErrorCode

ssi_sdk.enums.ServerErrorCode liệt kê các mã lỗi ứng dụng mà server trả trong {"code", "msg"} (ví dụ OTP_VERIFICATION_FAILED = 401100, REFRESH_TOKEN_REVOKED_OR_EXPIRED = 401103, PUSH_APPROVAL_REJECTED = 401115, MODIFY_PRICE_AND_QUANTITY = 400107, REPLAY_WINDOW_EXCEEDED = 400111, TIMEFRAME_NOT_SUPPORTED = 400210). Một số số được dùng lại cho nhiều ý nghĩa (ví dụ 400104 vừa là "không gửi được OTP" vừa là "thiếu symbol"), nên tên sau là alias của tên đầu. Các mã chữ ký (4010xx) không khớp hoàn toàn với error-code-mapping.json phía server: hãy tin số mã, không tin text đã map.

from ssi_sdk.enums import ServerErrorCode

try:
    ...
except APIError as e:
    if ServerErrorCode.from_value(e.code) == ServerErrorCode.REPLAY_WINDOW_EXCEEDED:
        print("Request nằm ngoài cửa sổ thời gian cho phép")

from_value trả về None với mã không có trong enum, nên không bao giờ raise. HTTPStatus (cùng module) là IntEnum các HTTP status mà API dùng (có is_auth_error, is_rate_limit, is_client_error, is_server_error).

Gợi ý xử lý

  • Bắt SSIError ở lớp ngoài cùng để gom lỗi chung.
  • Bắt AuthenticationError khi cần refresh token hoặc yêu cầu OTP mới; bắt các lớp con (SmartOTPPendingError, SmartOTPRejectedError, ReauthenticationRequired) trước nếu cần xử lý riêng.
  • Bắt RateLimitError và tôn trọng retry_after_ms để backoff.
  • Bắt APIError để xử lý lỗi nghiệp vụ từ server. HTTP 200 kèm code khác 200/204 cũng được ném thành APIError.
  • Chỉ request GET được SDK tự retry; POST/PUT/DELETE (đặt lệnh, huỷ lệnh, batch) không bao giờ bị retry tự động.
  • Lỗi từng lệnh trong batch order là dữ liệu trong BatchOrderResult, không phải exception. Xem Batch Order.
  • Bắt WebSocketError khi xử lý streaming; Trading WebSocket ném TradingWSError.
  • Bắt ValidationError khi cần xác thực input trước khi gọi API.

Trên trang này