Перейти к содержанию

Руководство по обработке ошибок: Saga API

Аудитория: разработчики, backend-инженеры, frontend-разработчики Последнее обновление: 2025-11-17 Краткое содержание: Полный справочник по обработке ошибок Saga API — структура error responses, HTTP status codes, comprehensive error code reference, troubleshooting guides для каждой категории ошибок.


Структура ответа с ошибкой

Стандартный формат ошибки

Все API ошибки возвращаются в унифицированном формате:

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description",
    "details": {
      "field": "problematic_field",
      "reason": "detailed_explanation",
      "suggestion": "how_to_fix"
    }
  },
  "timestamp": "2025-10-03T12:00:01Z"
}

Описание полей:

  • success: false для всех error responses
  • error.code: Машиночитаемый код ошибки (uppercase, snake_case)
  • error.message: Понятное пользователю описание проблемы
  • error.details: Дополнительная контекстная информация (опционально)
  • timestamp: ISO 8601 timestamp когда произошла ошибка

Примеры ответов с ошибками

Ошибка валидации:

{
  "success": false,
  "error": {
    "code": "INVALID_AMOUNT",
    "message": "Investment amount must be at least 10 USDC",
    "details": {
      "field": "amount",
      "provided": "5.00",
      "minimum": "10.00",
      "suggestion": "Increase investment amount to at least 10 USDC"
    }
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

Ошибка аутентификации:

{
  "success": false,
  "error": {
    "code": "TOKEN_EXPIRED",
    "message": "Authentication token has expired",
    "details": {
      "expiredAt": "2025-10-02T12:34:56Z",
      "suggestion": "Re-authenticate to obtain a new token"
    }
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

Ошибка бизнес-логики:

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient USDC balance for investment",
    "details": {
      "requested": "1000.00 USDC",
      "available": "750.50 USDC",
      "shortfall": "249.50 USDC",
      "suggestion": "Deposit more USDC or reduce investment amount"
    }
  },
  "timestamp": "2025-10-03T12:34:56Z"
}

HTTP Status Codes

Использование кодов статуса

Saga API использует стандартные HTTP status codes:

Код статуса Категория Использование
200 OK Success Успешные GET, PUT, PATCH запросы
201 Created Success Успешный POST создающий новый ресурс
204 No Content Success Успешный DELETE (без тела ответа)
400 Bad Request Client Error Ошибки валидации, некорректный ввод
401 Unauthorized Client Error Отсутствует или некорректная аутентификация
403 Forbidden Client Error Валидная auth, но недостаточно прав
404 Not Found Client Error Ресурс не существует
409 Conflict Client Error Конфликт ресурса (дубликат, заблокирован)
422 Unprocessable Entity Client Error Ошибки семантической валидации
429 Too Many Requests Client Error Превышен rate limit
500 Internal Server Error Server Error Неожиданная ошибка на стороне сервера
503 Service Unavailable Server Error Временная недоступность, обслуживание

Дерево решений для кодов статуса

Запрос успешен?
├─ ДА → 2xx коды статуса
│   ├─ Создан новый ресурс? → 201 Created
│   ├─ Удалён ресурс? → 204 No Content
│   └─ Другой успех → 200 OK
└─ НЕТ → Коды ошибок
    ├─ Ошибка клиента?
    │   ├─ Некорректный ввод? → 400 Bad Request
    │   ├─ Auth отсутствует? → 401 Unauthorized
    │   ├─ Недостаточно прав? → 403 Forbidden
    │   ├─ Ресурс не найден? → 404 Not Found
    │   ├─ Конфликт ресурса? → 409 Conflict
    │   ├─ Ошибка бизнес-логики? → 422 Unprocessable Entity
    │   └─ Превышен rate limit? → 429 Too Many Requests
    └─ Ошибка сервера?
        ├─ Временная проблема? → 503 Service Unavailable
        └─ Неожиданная ошибка → 500 Internal Server Error

Справочник кодов ошибок

Ошибки аутентификации (401/403)

TOKEN_EXPIRED - HTTP Status: 401 Unauthorized - Message: "Authentication token has expired" - Причина: JWT токен старше 24 часов - Решение: Повторная аутентификация для получения нового токена - Предотвращение: Реализовать обновление токена перед истечением

TOKEN_INVALID - HTTP Status: 401 Unauthorized - Message: "Invalid authentication token" - Причина: Неправильный формат JWT, некорректная подпись, или изменённый токен - Решение: Повторная аутентификация с валидными credentials - Предотвращение: Никогда не изменять JWT токены на стороне клиента

TOKEN_MISSING - HTTP Status: 401 Unauthorized - Message: "Authentication token required" - Причина: Authorization header отсутствует - Решение: Добавить Authorization: Bearer <token> header - Предотвращение: Проверять auth state перед API вызовами

SIGNATURE_INVALID - HTTP Status: 401 Unauthorized - Message: "Wallet signature verification failed" - Причина: Подписанное сообщение не соответствует адресу кошелька - Решение: Убедиться что подключён правильный кошелёк, переподписать сообщение - Предотвращение: Проверять адрес кошелька перед подписью

NONCE_EXPIRED - HTTP Status: 401 Unauthorized - Message: "Authentication nonce has expired" - Причина: Challenge nonce старше 5 минут - Решение: Запросить новый challenge и подписать немедленно - Предотвращение: Завершить auth flow в течение 5 минут

PERMISSION_DENIED - HTTP Status: 403 Forbidden - Message: "Insufficient permissions for this operation" - Причина: Пользователю не хватает необходимой роли или разрешения - Решение: Связаться с админом для предоставления прав - Предотвращение: Проверять права пользователя перед попыткой действия

ADMIN_REQUIRED - HTTP Status: 403 Forbidden - Message: "Admin privileges required" - Причина: Endpoint требует admin токен, предоставлен user токен - Решение: Использовать admin authentication flow - Предотвращение: Использовать правильный auth flow для admin endpoints


Ошибки валидации (400/422)

INVALID_AMOUNT - HTTP Status: 422 Unprocessable Entity - Message: "Invalid investment/withdrawal amount" - Причина: Сумма ниже минимума, выше максимума, или отрицательная - Решение: Скорректировать сумму в разрешённом диапазоне - Детали: - Минимальная инвестиция: 10 USDC - Максимальная инвестиция: 1,000,000 USDC - Должна быть положительным числом

INVALID_EMAIL - HTTP Status: 400 Bad Request - Message: "Invalid email address format" - Причина: Email не соответствует RFC 5322 формату - Решение: Предоставить валидный email (user@domain.com) - Предотвращение: Валидировать email на клиенте перед отправкой

INVALID_WALLET_ADDRESS - HTTP Status: 400 Bad Request - Message: "Invalid Ethereum wallet address" - Причина: Адрес не 42-символьная hex строка начинающаяся с 0x - Решение: Предоставить валидный Ethereum адрес - Предотвращение: Валидировать формат адреса (ethers.utils.isAddress)

INVALID_STRATEGY - HTTP Status: 422 Unprocessable Entity - Message: "Invalid investment strategy" - Причина: Strategy ID не существует или deprecated - Решение: Использовать валидную стратегию: "conservative-5", "balanced-10", "aggressive-20" - Предотвращение: Получить доступные стратегии от /api/user/strategies

MISSING_REQUIRED_FIELD - HTTP Status: 400 Bad Request - Message: "Required field missing: {field_name}" - Причина: Обязательное поле отсутствует в request body - Решение: Включить все обязательные поля - Предотвращение: Валидировать request schema на клиенте

INVALID_DATE_RANGE - HTTP Status: 400 Bad Request - Message: "Invalid date range" - Причина: Дата начала после даты окончания, или будущие даты - Решение: Предоставить валидный диапазон дат (start ≤ end, обе ≤ сегодня) - Предотвращение: Валидировать даты перед отправкой


Ошибки бизнес-логики (422)

INSUFFICIENT_BALANCE - HTTP Status: 422 Unprocessable Entity - Message: "Insufficient USDC balance for operation" - Причина: Баланс пользователя < запрошенной суммы - Решение: Пополнить USDC или уменьшить сумму - Детали: Включает available, requested, shortfall суммы

INVESTMENT_LOCKED - HTTP Status: 422 Unprocessable Entity - Message: "Investment locked in cooldown period" - Причина: Недавняя смена стратегии, нужно дождаться cooldown - Решение: Подождать до истечения cooldown (обычно 24 часа) - Детали: Включает lockedUntil timestamp

STRATEGY_NOT_AVAILABLE - HTTP Status: 422 Unprocessable Entity - Message: "Selected strategy currently unavailable" - Причина: Стратегия приостановлена из-за рыночных условий или обслуживания - Решение: Выбрать альтернативную стратегию - Предотвращение: Проверить статус стратегии перед инвестицией

WITHDRAWAL_LIMIT_EXCEEDED - HTTP Status: 422 Unprocessable Entity - Message: "Daily withdrawal limit exceeded" - Причина: Пользователь достиг дневного лимита на вывод - Решение: Подождать до следующего дня или запросить увеличение лимита - Детали: Включает dailyLimit, usedToday, available суммы

DUPLICATE_INVESTMENT - HTTP Status: 409 Conflict - Message: "Investment already exists for this strategy" - Причина: Пользователь уже имеет активную инвестицию в этой стратегии - Решение: Использовать существующую инвестицию или создать новую стратегию - Предотвращение: Проверить активные инвестиции пользователя сначала

INVESTMENT_NOT_FOUND - HTTP Status: 404 Not Found - Message: "Investment not found" - Причина: Investment ID не существует или принадлежит другому пользователю - Решение: Проверить investment ID, убедиться в праве владения - Предотвращение: Получить инвестиции пользователя от /api/user/investments


Ошибки Blockchain (422/503)

BLOCKCHAIN_ERROR - HTTP Status: 503 Service Unavailable - Message: "Blockchain operation failed" - Причина: RPC нода недоступна, перегрузка сети, или ошибка контракта - Решение: Повторить запрос после короткой задержки - Детали: Включает blockchain error message

WALLET_CONNECTION_ERROR - HTTP Status: 422 Unprocessable Entity - Message: "Failed to connect to wallet" - Причина: MetaMask не установлен или заблокирован - Решение: Установить MetaMask, разблокировать кошелёк, обновить страницу - Предотвращение: Проверить window.ethereum перед подключением

GAS_ESTIMATION_FAILED - HTTP Status: 422 Unprocessable Entity - Message: "Unable to estimate gas for transaction" - Причина: Недостаточно gas, revert контракта, или некорректные параметры - Решение: Увеличить gas limit или проверить параметры транзакции - Детали: Включает estimated gas needed

TRANSACTION_FAILED - HTTP Status: 422 Unprocessable Entity - Message: "Blockchain transaction failed" - Причина: Транзакция reverted on-chain - Решение: Проверить transaction hash в explorer, повторить если временная проблема - Детали: Включает transaction hash, revert reason

NETWORK_MISMATCH - HTTP Status: 422 Unprocessable Entity - Message: "Wrong blockchain network" - Причина: Кошелёк подключён к неправильной сети (не Saga Testnet) - Решение: Переключить кошелёк на Saga Testnet (chainId: 1337) - Предотвращение: Автоматически предлагать смену сети при подключении


Ошибки Rate Limiting (429)

RATE_LIMIT_EXCEEDED - HTTP Status: 429 Too Many Requests - Message: "Too many requests, please try again later" - Причина: Пользователь превысил часовой rate limit - Решение: Подождать пока rate limit сбросится - Детали: - Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset - Retry-After header с секундами ожидания

Лимиты для конкретных Endpoint:

  • User API: 1000 запросов/час на пользователя
  • Admin API: 10,000 запросов/час на админа
  • Public API: 100 запросов/час на IP
  • Faucet: 10 запросов/день на адрес кошелька

Ошибки сервера (500/503)

INTERNAL_SERVER_ERROR - HTTP Status: 500 Internal Server Error - Message: "An unexpected error occurred" - Причина: Необработанное исключение сервера - Решение: Связаться с поддержкой если сохраняется - Детали: Error ID для ссылки в поддержке

DATABASE_ERROR - HTTP Status: 503 Service Unavailable - Message: "Database temporarily unavailable" - Причина: Пул соединений БД исчерпан или обслуживание - Решение: Повторить запрос после короткой задержки - Предотвращение: Реализовать exponential backoff

SERVICE_UNAVAILABLE - HTTP Status: 503 Service Unavailable - Message: "Service temporarily unavailable" - Причина: Запланированное обслуживание или неожиданный downtime - Решение: Проверить status page, повторить позже - Детали: Включает maintenanceWindow если запланировано


Лучшие практики обработки ошибок

Для Frontend разработчиков

1. Всегда проверяйте поле success:

const response = await fetch('/api/user/balance');
const data = await response.json();

if (!data.success) {
  // Обработка ошибки
  console.error(`Error: ${data.error.code} - ${data.error.message}`);
  showErrorToUser(data.error.message);
  return;
}

// Продолжить с success case
const balance = data.data.balance;

2. Реализуйте переключение по типу ошибки:

function handleApiError(error: ApiError) {
  switch (error.code) {
    case 'TOKEN_EXPIRED':
      // Перенаправить на логин
      router.push('/login');
      break;

    case 'INSUFFICIENT_BALANCE':
      // Показать модальное окно пополнения
      showDepositModal(error.details.shortfall);
      break;

    case 'RATE_LIMIT_EXCEEDED':
      // Показать обратный отсчёт повтора
      const retryAfter = error.details.retryAfter;
      showRetryCountdown(retryAfter);
      break;

    default:
      // Общее сообщение об ошибке
      showErrorToast(error.message);
  }
}

3. Реализуйте логику повтора с экспоненциальной задержкой:

async function apiCallWithRetry(url: string, options: RequestInit, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      const data = await response.json();

      if (response.status === 503 || response.status === 429) {
        // Сервер недоступен или rate limited - повторить
        const delay = Math.min(1000 * Math.pow(2, i), 10000); // Макс 10s
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      return data;
    } catch (error) {
      if (i === maxRetries - 1) throw error; // Последняя попытка провалилась
    }
  }
}

4. Отображайте понятные пользователю ошибки:

// ❌ ПЛОХО: Показать сырые коды ошибок
alert('Error: INVALID_AMOUNT');

// ✅ ХОРОШО: Показать чёткие, действенные сообщения
showNotification({
  type: 'error',
  title: 'Некорректная сумма инвестиции',
  message: 'Пожалуйста инвестируйте минимум 10 USDC. Вы ввели 5.00 USDC.',
  action: {
    label: 'Скорректировать сумму',
    onClick: () => focusAmountInput()
  }
});

Для Backend разработчиков

1. Всегда возвращайте структурированные ошибки:

// ✅ ХОРОШО: Структурированный ответ с ошибкой
func (h *InvestmentHandler) CreateInvestment(c *gin.Context) {
    if amount < minInvestment {
        c.JSON(422, gin.H{
            "success": false,
            "error": gin.H{
                "code": "INVALID_AMOUNT",
                "message": fmt.Sprintf("Investment amount must be at least %.2f USDC", minInvestment),
                "details": gin.H{
                    "field": "amount",
                    "provided": amount.String(),
                    "minimum": minInvestment.String(),
                    "suggestion": "Increase investment amount to at least 10 USDC",
                },
            },
            "timestamp": time.Now().UTC().Format(time.RFC3339),
        })
        return
    }
}

2. Включайте полезный контекст в ошибки:

// ❌ ПЛОХО: Расплывчатая ошибка
return errors.New("insufficient balance")

// ✅ ХОРОШО: Детальный контекст
return fmt.Errorf("insufficient balance: requested %s USDC, available %s USDC (shortfall: %s USDC)",
    requested.String(), available.String(), shortfall.String())

3. Логируйте ошибки для отладки:

if err := db.CreateInvestment(investment); err != nil {
    log.WithFields(log.Fields{
        "userID": userID,
        "amount": amount.String(),
        "strategyID": strategyID,
        "error": err.Error(),
    }).Error("Failed to create investment")

    c.JSON(500, gin.H{
        "success": false,
        "error": gin.H{
            "code": "DATABASE_ERROR",
            "message": "Failed to process investment",
            "errorID": generateErrorID(), // Для ссылки в поддержке
        },
    })
    return
}

Тестирование сценариев ошибок

Примеры cURL

Вызвать ошибку валидации:

curl -X POST https://app.saga.surf/api/user/investments \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "strategyId": "balanced-10",
    "amount": "5.00"
  }'
# Ожидается: 422 INVALID_AMOUNT (ниже минимума 10 USDC)

Вызвать ошибку аутентификации:

curl -X GET https://app.saga.surf/api/user/balance
# Ожидается: 401 TOKEN_MISSING

Вызвать Rate Limit:

for i in {1..1100}; do
  curl -X GET https://app.saga.surf/api/user/balance \
    -H "Authorization: Bearer $TOKEN"
done
# Ожидается: 429 RATE_LIMIT_EXCEEDED (после 1000 запросов)

Вызвать ошибку бизнес-логики:

curl -X POST https://app.saga.surf/api/user/investments \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "strategyId": "balanced-10",
    "amount": "999999.00"
  }'
# Ожидается: 422 INSUFFICIENT_BALANCE (если баланс пользователя < 999999)

Связанная документация

Справочники API:

Поддержка:




📋 Метаданные

Версия: 2.6.268

Обновлено: 2025-10-21

Статус: Published