Ошибки
Форма ошибки, несколько правил, с которыми отказы безопасно обрабатывать, и все коды ошибок API v2 в одном месте.
Форма ответа
Успех — объект с "success": true плюс поля самой ручки. Ошибка — "success": false, машинный код в error, объяснение в message и поля, специфичные для этой ошибки: неверное значение, допустимые значения, кандидаты на выбор:
{"success":false,"error":"page_not_found","message":"there is no page with the slug \"nope-nope\"","slug":"nope-nope"}
{"success":false,"error":"reserved_dataset_name","message":"the name \"menu.header\" belongs to the platform: the menu.*, site.*, pages.* namespaces and breadcrumbs are reserved for system sources and cannot be overwritten by a dataset","reserved":true}
{
"success": false,
"error": "invalid_slug",
"message": "slug \"site_header\" does not match the naming scheme block__element--modifier: …",
"slug": "site_header",
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*(?:__[a-z0-9]+(?:-[a-z0-9]+)*)?(?:--[a-z0-9]+(?:-[a-z0-9]+)*)?$",
"hint": "block__element--modifier: lowercase latin letters and digits, …"
}
Ветвитесь по error, никогда по message. Текст пишется для человека и меняется, код — контракт.
Ошибки в форме WordPress
Часть отказов случается раньше, чем запрос доходит до ручки, и они приходят в стандартной форме WordPress {code, message, data.status}, без поля success. Это авторизация, выбор сайта, лимит запросов и неизвестные маршруты. Сообщения у них русские — ещё одна причина читать только код:
{"code":"missing_token","message":"Нужен API-токен: заголовок Authorization: Bearer <token> или X-OnPress-Key","data":{"status":401}}
{"code":"invalid_token","message":"Неверный или отозванный API-токен","data":{"status":401}}
{"code":"site_not_found","message":"в сети нет сайта с доменом \"nope.example.com\"","data":{"status":404}}
{"code":"not_found","message":"Маршрут не найден","data":{"status":404}}
Клиент, который понимает обе формы, читает error, когда success равен false, и code в остальных случаях.
Запрос выполняется целиком или не выполняется
Это принцип, а не свойство отдельных ручек. Запрос, который нельзя выполнить ровно так, как просили, падает до того, как что-то сделано наполовину: страница не создаётся, родитель не выдумывается, язык не переназначается, дерево не перестраивается. После неудачного вызова подчищать нечего — исправьте тело и повторите.
Там, где раньше поведение было мягким, возврат к нему делается явным флагом: create_missing_parents, link_soft, allow_invalid, "missing": "keep". Тогда неаккуратный результат — это то, что вы попросили, а не то, что с вами случилось.
Ручки, которые пишут много, умеют предварительный прогон: "dry_run": true у POST /frames, PUT /site/css, PATCH /site/design, PUT /data/{name}, PUT /docs/sidebar и POST /pages/language. Ответ показывает все изменения, в базу ничего не пишется.
Предупреждения — часть ответа
Успешный ответ несёт warnings — фразы о том, что не так, но не помешало. Их надо читать: именно там живут «страница не опубликована, адрес её не отдаёт», «template устарел», «data-op-bind в двойных кавычках», «неизвестный ключ frontmatter сохранён в мету» и «op/frame ссылается на несуществующий слаг». Пустой warnings — тоже информация.
Общие коды
| Код | HTTP | Форма | Что значит |
|---|---|---|---|
missing_token | 401 | WordPress | не прислан ни Authorization: Bearer, ни X-OnPress-Key |
invalid_token | 401 | WordPress | токен неизвестен или перевыпущен |
forbidden_site | 403 | WordPress | пользователь не состоит в выбранном сайте |
site_not_found | 404 | WordPress | domain не называет ни одного сайта сети |
not_found | 404 | WordPress | такого маршрута нет |
too_many_requests | 429 | WordPress | больше 120 запросов в минуту; см. Retry-After |
auth_error | 500 | WordPress | токен не удалось проверить |
forbidden | 403 | v2 | ручке мало членства: нужен администратор сайта или суперадмин сети |
nothing_to_update | 400 | v2 | PATCH или запись, в которой нечего менять |
page_not_found | 404 | v2 | ссылка на страницу (id, слаг или путь) ни во что не разрешилась |
ambiguous_page | 409 | v2 | слагом или путём владеют несколько страниц; они в candidates |
db_error | 500 | v2 | база отказала в записи; код драйвера в db_code |
internal_error | 500 | v2 | необработанная ошибка сервиса; подробности в логе |
Страницы и markdown
POST /pages/markdown и POST /pages/language. Подробно — Страницы и markdown.
| Код | HTTP | Когда | Выход |
|---|---|---|---|
absolute_url_in_path | 422 | в url схема или домен | передать путь |
parent_not_found | 422 | сегмент url не разрешился | create_missing_parents: true |
parent_create_failed | 500 | запрошенную заглушку родителя не удалось записать | — |
ambiguous_page | 409 | слаг (плюс язык) совпал с несколькими страницами | передать id или lang, либо slug_lookup: false |
lang_mismatch | 409 | id указывает на страницу другого языка, чем lang | translation_of вместо id |
unknown_language | 422 | lang — не язык сайта; известные перечислены в ответе | — |
translation_link_failed | 422 | translation_of не держится; причина в reason | link_soft: true |
translation_group_conflict | 409 | страница уже состоит в другой группе переводов | PUT /pages/{ref}/translations |
front_page_group_conflict | 409 | то же, и страница — главная языка | — |
frame_not_found | 404 | frame не называет ни одного фрейма сайта | — |
frame_single | 400 | в frame больше одного фрейма или старая форма type=ref | один слаг или id |
gone | 410 | frame: default:<type> — убрано вместе с типами фреймов | frame: <slug> |
post_type_not_supported | 501 | type во frontmatter не page; v2 пишет только страницы | посты публиковать через v1 |
polylang_inactive | 409 | POST /pages/language на сайте без Polylang | — |
reason у translation_link_failed — одно из polylang_missing, lang_required, origin_not_found, origin_has_no_language, same_language. Если группу переводов отвергают сами её члены, тот же код приходит с 409 и списком rejected. Тело без строки markdown — 400, где объяснение лежит прямо в error, а отдельного кода нет.
Переводы
GET, PUT, POST, DELETE /pages/{ref}/translations. Подробно — Переводы.
| Код | HTTP | Когда |
|---|---|---|
bad_translations | 400 | translations — не объект |
page_required | 400 | POST …/translations/{lang} без page |
unknown_language | 422 | такого языка на сайте нет |
translations_rejected | 409 | часть записей неверна; rejected перечисляет каждую с lang, ref, reason |
page_in_other_group | 409 | страница из списка состоит в другой группе с другими членами |
page_has_no_language | 409 | у страницы из адреса нет языка; чинится POST /pages/language |
same_language | 409 | {lang} — язык самой страницы из адреса |
page_language_mismatch | 409 | переданная страница другого языка; в ответе actual_lang |
polylang_inactive | 409 | на сайте нет Polylang |
Сайдбар документации
/docs/sidebar и ручки его пунктов. Подробно — Сайдбар документации.
| Код | HTTP | Когда |
|---|---|---|
bad_items | 400 | items — не массив |
bad_missing | 400 | missing — ни hide, ни keep |
bad_reorder | 400 | в reorder нет ни order, ни id |
item_ref_required | 400 | пункт назван пустой или неразборчивой ссылкой |
not_a_page | 404 | ссылка разрешилась в запись другого типа |
page_language_mismatch | 404 | у пути один языковой префикс, а найденная страница другого языка |
unknown_language | 422 | lang — не язык сайта |
absolute_url_in_path, parent_not_found | 422 | как при публикации |
duplicate_item | 409 | одна страница дважды в дереве |
not_a_sibling | 409 | в order страница из другой ветки |
sidebar_items_unresolved | 409 | общая ошибка PUT: ничего не применено, разбор в errors |
Фреймы
/frames и /frames/rebake. Подробно — Фреймы и Как это устроено.
| Код | HTTP | Когда |
|---|---|---|
frame_html_required | 400 | нет html или это не строка |
invalid_slug | 400 | слаг не по схеме block__element--modifier; в ответе pattern и hint |
frame_url_not_supported | 400 | url в теле POST, PUT или PATCH /frames: фрейм — шаблон, адрес принадлежит странице |
page_required | 400 | assign без page |
nothing_to_rebake | 400 | POST /frames/rebake ничего не называет |
frame_not_found | 404 | на сайте нет фрейма с таким id или слагом |
slug_taken | 409 | этот слаг уже у другого фрейма сайта |
gone | 410 | /frames/defaults, /frames/{ref}/default, PATCH с одним is_default |
frame_too_large | 413 | разметка больше 512 КБ |
Реальный ответ:
{"success":false,"error":"gone","message":"default frames by type are gone (frames v3): a page is bound to its frame with POST /frames/{ref}/assign or frontmatter \"frame\", and frames link to each other with <!-- wp:op/frame {\"ref\":\"site__header\"} /-->"}
Данные
/data, /data/sources, /data/resolve. Подробно — Данные.
| Код | HTTP | Когда |
|---|---|---|
dataset_name_required | 400 | имя не передано |
bad_dataset_name | 400 | имя не подходит под [a-z0-9][a-z0-9_-]{0,63} |
bad_dataset_items | 400 | items — ни массив, ни объект |
bad_patch_op | 400 | неизвестная операция или операция без данных |
item_ref_required | 400 | элемент не адресован ни index, ни match |
match_token_unsupported | 400 | в match токен, который сервис не умеет вычислить; те, что умеет, — в supported |
dataset_not_found | 404 | набора с таким именем нет |
item_not_found | 404 | нет элемента с таким индексом или под такой фильтр |
source_not_found | 404 | имя — ни системный источник, ни набор |
reserved_dataset_name | 409 | имя принадлежит платформе |
dataset_too_large | 413 | больше 512 КБ |
Стили
/site/css и /site/design. Подробно — Стили.
| Код | HTTP | Когда |
|---|---|---|
css_required | 400 | в теле нет строки css; очистить — DELETE |
css_too_large | 413 | больше 256 КБ; в ответе bytes и max_bytes |
unknown_token | 400 | такого токена дизайна нет; список в GET /site/design |
bad_token_value | 400 | значение не подходит под тип или диапазон токена |
{"success":false,"error":"bad_token_value","message":"\"h1.size\": a number is expected, got \"big\"","token":"h1.size"}
Меню
/menus. Подробно — Меню.
| Код | HTTP | Когда |
|---|---|---|
menu_name_required | 400 | POST /menus без имени |
menu_ref_required | 400 | не передана ссылка на меню |
location_required | 400 | назначение без области |
unknown_location | 400 | тема не регистрирует такую область |
unknown_language | 400 | такого языка на сайте нет |
bad_items, bad_reorder | 400 | неверные items или reorder |
menu_not_found | 404 | нет меню с таким id, слагом или именем |
menu_not_assigned | 404 | использован ~область, но туда ничего не назначено |
item_not_found | 404 | такого пункта в меню нет или он не на этом уровне |
ambiguous_menu | 409 | именем владеют несколько меню |
menu_items_unresolved | 409 | часть пунктов не разрешилась; ничего не записано |
menu_items_invalid | 409 | часть пунктов ведёт на страницы, которых посетитель не увидит; allow_invalid пишет всё равно |
bad_parent | 409 | пункт сделан собственным родителем |
polylang_inactive | 409 | язык передан, а Polylang на сайте нет |
menu_assignment_not_stored | 500 | назначение области не сохранилось |
Медиа
/media. Подробно — Медиа.
| Код | HTTP | Когда |
|---|---|---|
source_required, source_ambiguous | 400 | нет ни url, ни data, или есть оба |
filename_required | 400 | data без filename |
bad_url | 400 | не http(s), с учётными данными или указывает на локальный или приватный хост |
bad_data, bad_field | 400 | data не base64; alt, title или lang — не строка |
unknown_language | 400 | такого языка на сайте нет |
source_url_required | 400 | GET /media без source_url или sha256 |
bad_source_url, bad_sha256, bad_id | 400 | неверный параметр поиска |
media_not_found | 404 | нет вложения с таким источником, хешем или id |
file_too_large | 413 | больше 15 МБ |
unsupported_type | 415 | не JPEG, PNG, WebP, GIF или SVG |
empty_file | 400 | ноль байт |
unsafe_svg, source_not_found, download_failed, sideload_failed | 422 | файл или его источник отвергнут; причина в message |
media_failed, media_unavailable | 502, 503 | PHP-сторона ответила неожиданно или не развёрнута |
Сайты и настройки
/sites, /site/settings, /site/front-page, /site/cache/flush. Подробно — Сайты и настройки.
| Код | HTTP | Когда |
|---|---|---|
forbidden | 403 | создание сайта не суперадмином сети; настройки не администратором сайта |
invalid_name, invalid_donor, invalid_keep_frames | 400 | неверные поля POST /sites |
donor_not_found | 404 | нет такого сайта-донора |
site_exists | 409 | домен занят; в ответе blog_id, повторять безопасно |
purge_failed | 500 | клон создан, но очистка остановилась |
unknown_field, invalid_analytics, invalid_site_icon | 400 | неверные поля PATCH /site/settings |
nothing_to_change | 400 | PATCH /site/settings, в котором нечего менять |
invalid_page, invalid_lang | 400 | неверные поля PUT /site/front-page |
no_default_translation | 409 | у главной нет перевода на язык по умолчанию |
invalid_transients, nothing_to_flush | 400 | неверный или пустой POST /site/cache/flush |
internal_unavailable, internal_failed | 502 | PHP-сторона не развёрнута или упала |
Язык сообщений
Сообщения ручек v2 английские. Ошибки в форме WordPress — авторизация, выбор сайта, лимит, неизвестный маршрут — русские, как и несколько сообщений, приходящих с PHP-стороны, например media_not_found. Если ветвиться по коду, всё это не имеет значения.