Skip to content

Коды ошибок (V2 и ACP)

В V2 и ACP все ошибки возвращаются в формате:

json
{
  "time": 1700000000,
  "error": {
    "code": "string",
    "message": "string",
    "details": { "key": "value" }
  }
}
  • code: машинночитаемый код.
  • message: краткое описание.
  • details: детали (валидатор, поля и т.п.); присутствует всегда, но может быть null.

HTTP-статус передаётся только в статус-строке ответа и в теле не дублируется.

Поле message не привязано к таблицам ниже: если доменная ошибка несёт собственное описание, оно подставляется в message поверх дефолтного текста бакета (not_found, invalid, conflict, unsupported, internal), поэтому клиент и логи видят конкретную причину, а не общий текст. Неклассифицированная ошибка тоже отдаётся со статусом 500 и кодом system.internal, но message несёт её реальный текст, а не заглушку «Internal server error». Ошибки разбора и валидации тела запроса приводятся к validation.invalid_input (422).

Источники:

  • тип ошибки API и конструкторы: interfaces/api/apierrors/app_error.go — тонкий ре-экспорт общей библиотеки shared/apierrors
  • маппинг доменных ошибок в ответ и выбор HTTP-статуса: shared/apierrors (Map, StatusForCode)
  • рендер тела ответа: middleware interfaces/api/middlewares/error_handler.go (делегирует в shared/apierrors)
  • доменные коды: domain/errors/errors.go — ре-экспорт shared/domainerr; MARV-специфичные расширения (version vector) в domain/errors/version_conflict.go

Стандартные ошибки API (apierrors)

codehttp_statusmessage
auth.unauthorized401Unauthorized
auth.invalid_headers400Invalid headers
auth.invalid_token401Invalid token
auth.token_expired401Token expired
auth.session_replaced401Session replaced by a newer login
auth.client_version_too_low400Client version too low
auth.banned403User is banned
auth.forbidden403Forbidden
validation.invalid_input422Invalid input
validation.conflict409Conflict
system.internal500Internal server error
system.try_again_later503Please try again later
db.select_failed500Database select failed
db.update_failed500Database update failed

Дополнительно при маппинге доменных ошибок:

codehttp_statusmessageисточник
resource.not_found404Not foundmapper
system.unsupported501Not implementedmapper

Ошибки привязки идентичностей (identity)

Возвращаются эндпоинтами портируемых аккаунтов (/v2/identities/connect, /disconnect, /resolve-conflict). Определены в interfaces/api/controllers/v2/identities.go.

codehttp_statusmessage
identity.conflict409Login belongs to another account
identity.last_identity422Cannot remove the last identity
identity.last_strong422Cannot remove the last strong login while a guest login remains
identity.not_owned404Identity not linked to this account
identity.unauthorized_link422Cannot link a guest login to a secured account

Доменные коды (domain/errors)

Эти коды маппятся в AppError через mapper.go:

domain codeНазначениеМаппинг в API
unauthorizedнет авторизацииauth.unauthorized (401)
forbiddenзапрет доступаauth.forbidden (403)
not_foundресурс не найденresource.not_found (404)
invalidневерные данныеvalidation.invalid_input (422)
conflictконфликт состоянияvalidation.conflict (409)
unsupportedне поддерживаетсяsystem.unsupported (501)
internalвнутренняя ошибкаsystem.internal (500)

Примечание: точный HTTP‑статус выбирается маппером, исходя из доменного кода.

Доменная ошибка может нести стабильный machine-код (slug) в поле code ответа — тогда клиент ветвится по нему, а message показывает человеку. HTTP‑статус всё равно берётся из бакета (conflict→409, invalid→422, forbidden→403 и т.д.).

Клановые коды (clans)

Специфичные code для клан-эндпоинтов (/v2/clans/*), чтобы клиент понимал причину:

codehttp_statusпричина
clan.tag_taken409Тег клана уже занят (глобально уникален)
clan.already_in_clan409Игрок уже состоит в клане
clan.duplicate_request409Активная заявка на вступление уже существует
clan.full422Клан заполнен (member_cap)
clan.not_open422Клан не open — прямой вход недоступен
clan.open_join_directly422Клан open — вступать нужно напрямую (не заявкой)
clan.invite_only422Клан только по приглашению
clan.request_limit422Превышен лимит активных заявок игрока
clan.not_in_clan422Игрок не состоит в клане
clan.leader_must_transfer422Лидер должен передать лидерство перед выходом
clan.name_tag_required422Имя и тег клана обязательны
clan.badwords422Имя/тег содержат запрещённые слова
clan.conflict409Прочий конфликт уникальности на клан-эндпоинте

Коды clan.tag_taken / clan.already_in_clan / clan.duplicate_request возвращаются в т.ч. на гонках (перевод Postgres unique_violation в 409, а не 500).

Коды клановых войн (clan_war)

Специфичные code для эндпоинтов войны (/v2/clans/war/*). Все — bucket invalid → HTTP 422; клиент ветвится по code, а не по общему validation.invalid_input.

codeHTTP статусОписание
clan_war.no_active_war422Активной войны нет
clan_war.no_active_phase422Нет активной фазы войны
clan_war.no_battle_phase422Текущая фаза — не боевая (battle)
clan_war.no_squad_phase422Отрядная фаза не активна
clan_war.not_points_phase422Текущая фаза — не очковая (points)
clan_war.battle_phase_over422Боевая фаза уже завершена
clan_war.not_in_battle_roster422Игрок не в боевом ростере фазы
clan_war.no_attacks422У игрока не осталось атак в фазе
clan_war.invalid_target422Некорректная цель атаки
clan_war.target_beaten422Цель уже побеждена в этой фазе
clan_war.battle_in_progress422Цель уже под атакой (эксклюзивный лок) — атака не тратится
clan_war.invalid_points422Некорректное значение очков (должно быть > 0)

Ошибки version vector (vclock)

Version vector используется для оптимистичного контроля конкурентности при обновлении data пользователя. Ошибки возвращаются в стандартном формате AppError c HTTP 409/422 и details содержащим обе версии:

json
{
  "time": 1700000000,
  "error": {
    "code": "version.dominated",
    "message": "Version dominated by server",
    "details": {
      "server_version_vector": { "server": 2, "xxx-device": 3000 },
      "client_version_vector": { "xxx-device": 2999 }
    }
  }
}
codehttp_statusописание
version.dominated409Сервер имеет более новую версию (incoming ≤ server)
version.concurrent409Конфликт: часть полей новее, часть старше
version.server_key_update409Клиент пытается инкрементить ключ «server»
version.unparsable422Невалидный формат version_vector

V1 маппинг: в V1 API те же ошибки возвращаются через числовые статус-коды:

V1 statuscodeописание
3001ErrorVersionDominatedСервер доминирует
3002ErrorVersionConcurrentКонкурентный конфликт
3003ErrorVersionInvalidНевалидный вектор
3004ErrorVersionServerKeyUpdateПопытка инкремента ключа «server»
3005ErrorVersionUnparsableНевозможно распарсить