Коды ошибок
Все ошибки API PageCraft — JSON одной формы, независимо от кода и эндпоинта. Кириллица не экранируется, читать ответ можно глазами.
Форма ответа
Заголовок раздела «Форма ответа»Минимальная:
{ "error": "Сообщение" }Там, где ошибку удаётся привязать к конкретному месту отправленного тела,
добавляется массив errors[]:
{ "error": "Ошибка валидации блоков", "errors": [ { "path": "blocks[0].placeholder_values.title", "message": "поле обязательно" }, { "path": "site_id", "message": "This value should be of type int." } ]}error— есть всегда, одно предложение о том, что не так в целом.errors[]— есть не всегда.path— dot-путь внутри payload (blocks[2].bindings.products.source) либо имя корневого поля; для ошибки без пути тамroot.
Правило работы с ответом: если errors[] пришёл — чинить нужно ровно то, на что
он указывает, и повторять запрос; текста error для этого недостаточно.
Таблица кодов
Заголовок раздела «Таблица кодов»| Код | Когда приходит | Что делать |
|---|---|---|
400 |
тело запроса не проходит проверку: отсутствует обязательное поле, placeholder_values не соответствуют схеме блока, q короче двух символов, файл не передан, публикация вызвана не тем методом |
разобрать errors[], исправить payload, повторить |
401 |
ключ не передан или не найден | см. API-ключи |
403 |
ресурс принадлежит другому пользователю и не является глобальным; либо аккаунт заблокирован | сменить ресурс — повтор не поможет |
404 |
сущность не существует или не видна вашему ключу | проверить идентификатор, подобрать замену |
405 |
метод не поддерживается этим путём | сверить метод со спецификацией |
409 |
конфликт состояния: slug занят, блок используется на страницах, заявка в каталог уже подана |
изменить значение или снять зависимость |
413 |
загружаемый файл больше 100 MB | уменьшить файл; лимит стоит на nginx, до приложения запрос не доходит |
422 |
тело синтаксически верно, но семантически невозможно: цикл в дереве папок, чужой parent_id, блок недоступен для коллекции, заблокированный major-апгрейд |
читать error/errors[] — это не про формат, а про смысл |
500 |
внутренняя ошибка | повторить позже; текст ошибки наружу не выдаётся, инцидент уже записан на стороне PageCraft |
502 |
внешняя система вернула ошибку: 4CMS при публикации, S3 при загрузке | сообщение провайдера — в теле ответа; проверить его настройки |
Разбор частых случаев
Заголовок раздела «Разбор частых случаев»401 — два разных ответа
Заголовок раздела «401 — два разных ответа»Ключ не передан вовсе:
{ "error": "API key required. Pass it via Authorization: Bearer <key>" }Ключ передан, но такого нет:
{ "error": "Invalid API key." }Второй ответ приходит и когда ключ удалён, и когда потерялся префикс pb_ —
сервер опознаёт ключевой запрос именно по Bearer pb_.
403 против 404
Заголовок раздела «403 против 404»Разница не в вашем доступе, а в существовании ресурса: 404 — «такого нет»,
403 — «есть, но чужое». Повторять запрос бессмысленно в обоих случаях.
Отдельный 403 — «Аккаунт заблокирован» на GET /sites: ключ рабочий,
но пользователю закрыт доступ.
409 при создании страницы
Заголовок раздела «409 при создании страницы»slug уникален в пределах сайта. При конфликте приходит:
{ "error": "Slug уже занят", "errors": [{ "path": "slug", "message": "..." }]}Тот же код — при попытке удалить блок, который стоит на страницах, или макет,
привязанный к сайту. Где именно используется блок, покажет
GET /blocks/{id}/usages.
422 на апгрейде блока
Заголовок раздела «422 на апгрейде блока»Особый случай с расширенным телом — блок обновился с ломающими изменениями:
{ "error": "major_upgrade_blocked", "message": "Block has breaking changes (v1.2 → v2.0). Migrate manually…", "current_pin": { "major": 1, "minor": 2 }, "library_version": { "major": 2, "minor": 0 }, "removed_fields": ["subtitle", "cta"]}Здесь error — код, а не текст; человекочитаемое сообщение в message.
Автоматически такое не применяется намеренно: значения удалённых полей
пропали бы молча. Порядок ручного переноса —
Устаревание и апгрейды.
Тот же сценарий на POST /pages/{pageId}/blocks/upgrade-all кодом не падает:
ответ 200, а проблемные блоки перечислены в skipped_major[].
400 на публикации
Заголовок раздела «400 на публикации»Три разных причины под одним кодом, различаются по тексту:
- «страница уже опубликована» — вызывайте
publish-update; - «страница ещё не публиковалась» — вызывайте
publish; - «у страницы нет сайта» — задайте
site_idчерезPUT /pages/{id}.
Первых двух легко избежать: перед публикацией спросите
GET /pages/{id}/publication — этот запрос локальный и во внешнюю систему
не ходит.
502 — ошибка провайдера
Заголовок раздела «502 — ошибка провайдера»PageCraft отработал, а внешняя система нет. Тело содержит сообщение, которое вернул провайдер: неверный токен 4CMS, несуществующая рубрика, отказ S3. Повтор помогает только при временном сбое; в остальных случаях чинится настройка сайта.
Что не считается ошибкой
Заголовок раздела «Что не считается ошибкой»blocks: nullв ответеGET /pages/{id}— блоки просто не запрашивались; добавьтеwith_blocks=1.- Пустой
binding_slotsу блока — блок статический,bindingsему не нужны. - Заглушки вместо реальных данных в предпросмотре — источники отдают
stubпо устройству, реальные данные появляются на витрине.
Источники
Заголовок раздела «Источники»backend/src/EventListener/ApiExceptionListener.php— единая форма тела ошибкиbackend/src/Security/ApiKeyAuthenticator.php— тексты ответов401backend/src/Exception/BlockPayloadValidationException.php(400+errors[])backend/src/Exception/SlugConflictException.php(409+errors[])openapi/v1.json— коды по эндпоинтам, схемаMajorUpgradeBlockedError