English · Русский
Обработка ошибок
См. также: index.md (индекс) · developer-guide.md (быстрый старт, выпуск ключа, примеры запросов) · security-model.md (localhost, модель ключей, аудит) · versioning-and-deprecation.md (политика semver).
Каждый ответ с ошибкой несёт тело JSON ApiError (Common.ApiError в контракте):
{
"error": "forbidden",
"message": "Insufficient tier or missing scope",
"required": { "tier": 2, "scopes": ["task:control"] },
"current": { "tier": 0, "scopes": ["task:read"] }
}
required/current (AuthRequirement: тир + скоупы) заполняются только при 403; при любом другом статусе они отсутствуют (только message, либо null).
Коды статусов и машиночитаемые коды ошибок
Большинство кодов ниже — это реальные константы из Common.ApiError.ApiErrorCodes / Common.ApiError.ApiStatus; два (not_implemented, service_unavailable) — буквальные строки, которые шлюз авторизации пишет напрямую, а не общие константы — это отдельно отмечено, поскольку это реальная, хоть и небольшая, несогласованность в текущем коде, а не ошибка документации. Если будущее изменение добавит код, добавляйте его сюда в том же PR.
| HTTP статус | код ошибки | Значение | Источник |
|---|---|---|---|
| 400 | bad_request |
Некорректно сформированный запрос (плохой JSON, неверный параметр). | Любая операция, валидация формы запроса. |
| 401 | unauthorized |
Отсутствует или невалиден Authorization: Bearer <api-key> — нет ключа, неизвестный ключ, либо истёкший/отключённый. |
Шлюз авторизации (AuthGate.Evaluate → AuthDecision.Unauthenticated), любая операция кроме ping. |
| 403 | forbidden |
Ключ валиден, но не хватает требуемого скоупа, либо тир операции превышает maxTier ключа. Тело включает required/current. |
Шлюз авторизации (OperationAuthorizer.Authorize → AuthDecision.Forbidden). |
| 404 | not_found |
Три разных источника, один код: (a) путь/глагол не сопоставляется ни с одной известной операцией; (b) на хосте ZennoPoster операция была распознана и авторизована, но задача, инстанс, вкладка или элемент, к которым она обращается, не существуют — например GET /tasks/{id} с неизвестным id; (c) на хосте ProjectMaker в открытом проекте нет действия, переменной, списка, таблицы или Google-таблицы с таким именем, либо файл в POST /projects/open не существует. «Проект вообще не открыт» — это не 404, см. project_not_open. |
(a) Шлюз авторизации; (b) обработчики домена ZennoPoster/Instance; (c) обработчики домена ProjectMaker. |
| 409 | project_not_open |
Любой операции /projects/current/* на хосте ProjectMaker нужен открытый проект, а в редакторе его нет (например, до открытия первого проекта). Сначала откройте или создайте проект (POST /projects/open, POST /projects). |
Обработчики домена ProjectMaker. |
| 409 | failed_precondition |
Открытый проект не в том состоянии, чтобы выполнить вызов: POST /projects/current/close при несохранённых изменениях без discardUnsavedChanges: true или во время выполнения/отладки; POST /projects/current/save, когда целевой файл существует и пользователь отказался его перезаписать. |
Обработчики домена ProjectMaker. |
| 404 | session_not_found |
GET/POST /api/v1/sessions/{id} — нет открытого окна WaitForUserAction с таким id. |
Домен сессий, только хост ZennoPoster. |
| 409 | no_active_interaction |
POST /sessions/{id}/complete обращается к окну WaitForUserAction, которое уже не открыто — уже завершено, либо окно/задача закрылись. Вызовы instance:interact эту ошибку не возвращают: управление вкладками/DOM не требует открытой сессии. |
Домен сессий, только хост ZennoPoster. |
| 409 | session_expired |
Объявлено в контракте для сессии, чьё окно закрылось до вызова complete. Пока ни одним путём кода не выбрасывается — ни одна сессия в текущей реализации не переходит в это состояние; считайте это зарезервированным на будущее, а не кодом, который вы реально увидите в v1. |
Домен сессий (зарезервировано). |
| 409 | dom_unavailable |
GET /instances/{id}/tabs/{tabId}/dom — текст DOM прямо сейчас не удалось получить (например, страница в процессе навигации). Повторите попытку, а не считайте это постоянным сбоем. |
Домен Instance, только хост ZennoPoster. |
| 409 | instance_busy |
DELETE /instances/{id} — порт принадлежит рабочему потоку выполняющейся задачи и не может быть освобождён через API. |
Домен Instance. |
| 409 | instance_view_protected |
POST /instances/{id}/show — вид браузера защищён (защита вида включена и нет открытого окна WaitForUserAction), окно не может быть показано. |
Домен Instance. |
| 409 | task_scheduler_owned |
DELETE /tasks/{id} — задача принадлежит джобу планировщика и не может быть удалена напрямую; удаляйте джоб планировщика. |
Домен задач, только хост ZennoPoster. |
| 413 | payload_too_large |
Тело запроса превышает лимит загрузки хоста (по умолчанию 2 ГБ — предохранитель, а не то, во что упирается обычное использование). Применяется к любой операции с телом JSON на хосте ZennoPoster. | Хост ZennoPoster, глобальная защита размера тела запроса (не бизнес-логика конкретной операции). |
| 429 | rate_limited |
Слишком много конкурентных long-poll’ов GET /sessions/events (лимит на хост, по умолчанию 32); также зарезервировано для rate-limiting удалённого режима. |
Long-poll событий сессий; удалённый режим/TLS (пока не включён). |
| 500 | internal_error |
Необработанный сбой на стороне сервера. | Любая операция. |
| 501 | not_implemented |
Шлюз распознаёт путь/глагол как объявленную операцию, но OperationDescriptor.IsImplemented == false — она не подключена к обработчику на этом хосте (например, HITL-трио confirmations_*). |
Шлюз авторизации (AuthDecision.NotImplemented). |
| 503 | service_unavailable |
Главный выключатель среды выполнения AI/PublicApi отключён — любой вызов отклоняется ещё до проверки ключа. | Шлюз авторизации, проверяется до деталей ключа/операции. |
Заметка о 409
Сам шлюз авторизации никогда не возвращает 409 — согласно собственной документации Auth.HttpListener, он выдаёт только 401 / 403 / 404 / 501 (осознанное решение: отказы авторизации не получают статус «конфликт»). 409 — статус уровня домена, возвращаемый только доменом sessions/instance, когда вызов вполне авторизован, но нужный ему контекст времени выполнения (открытое окно взаимодействия) отсутствует. Не путайте эти два случая: 403 означает «ваш ключ не может это сделать»; 409 означает «ваш ключ может это сделать, но не прямо сейчас».
Чек-лист обработки для интегратора
- 401 — ключ отсутствует, неверен, истёк или был отозван. Выпустите ключ повторно через UI; слоя обновления нет (непрозрачные ключи, без обмена токенами).
- 403 — сравните
requiredиcurrentв теле и либо запросите ключ с недостающим скоупом/тиром, либо не выполняйте этот вызов. Не повторяйте попытку без изменений; с тем же ключом она никогда не пройдёт. - 404 / 409 в домене sessions/instance — это ожидаемо, а не исключительно: окно
WaitForUserActionможет закрыться между вашимGET /sessionsи вызовомPOST /sessions/{id}/complete. Перед повтором заново получите список и убедитесь, что сессия ещё открыта. - 429 — снижайте частоту; актуально для long-poll
GET /sessions/events(лимит конкурентных поллов) и, в будущем, для удалённого режима. - 501 — операция объявлена в контракте, но не подключена на этом хосте (
isAvailable: falseв/capabilitiesдля этогоoperationId); не вызывайте её. - 503 — главный выключатель AI/PublicApi отключён на стороне хоста; ничто не заработает, пока его не включат снова. Клиент этого обойти не может.
- 409 dom_unavailable — временная ситуация; повторите вызов получения текста DOM, а не считайте это постоянным сбоем.
- 413 — тело вашего запроса превышает лимит загрузки хоста; это предохранитель (по умолчанию 2 ГБ), а не обычный лимит использования — если вы в него упёрлись, вероятно, что-то не так на стороне клиента.
Всегда сначала проверяйте GET /api/v1/capabilities (см. «Руководство разработчика»), чтобы избежать 403/501, которые можно было предсказать на стороне клиента.