# Ошибки обхода

> Коды отказов Crawler, что каждый значит и что с ним делать

Источник: https://docs.parreq.com/crawl-errors/

Отказы приходят в общем формате [раздела об ошибках](https://docs.parreq.com/errors): код, текст и
`request_id`. Ниже — только те, что относятся к обходу.

## Коды

| HTTP | `code` | когда | что делать |
|---|---|---|---|
| 400 | `invalid_request` | правило не годится: нет ни `follow`, ни `next`; `pages` вне 1…200; `depth` вне 0…5; `allow` или `deny` не списком строк; адрес без схемы или закрытый; ошибка в описании полей `extract` | починить запрос |
| 402 | `out_of_credits` | на полный обход (`credits_max`) не хватает баланса | пополнить либо уменьшить `pages` |
| 403 | `feature_not_enabled` | возможность `crawl` не подключена ключу | подключить |
| 404 | `not_found` | нет такого задания у этого ключа | проверить идентификатор |
| 409 | `already_finished` | отмена задания, которое уже закончилось | перечитать статус |
| 429 | `rate_limited`, `quota_exceeded`, `concurrency_limit` | частотный предел исчерпан | повторить по `Retry-After` |
| 503 | `service_unavailable` | хранилище заданий не отвечает | повторить позже |

> **Примечание.**
>   Всё, что проверяется до старта — правило, права, баланс, — **бесплатно**:
>   отказ не стоит ни кредита и до браузера не доходит. Селектор, который ни с
>   чем не совпал, — другое дело: задание примут, стартовую страницу возьмут и
>   за неё спишут.

## `invalid_request`: правило проверяется целиком и заранее

Обход из двухсот страниц с ошибкой в правиле — плохой способ узнать про
опечатку. Поэтому правило разбирается до того, как задание попало в очередь:
способ движения, диапазоны `pages` и `depth`, вид списков, схема начального
адреса, описание полей `extract`.

Регулярные выражения `allow` и `deny` тоже разбираются заранее: неразбираемое
выражение или выражение длиннее 200 символов дают `400` с указанием, какое
именно не годится. Длина ограничена не из придирчивости — выражение приходит
снаружи и исполняется на каждой найденной ссылке, а прерывать его по времени
нечем.

Чего проверка не ловит — селекторы: годятся они или нет, выясняется только на
живой странице. Селектор, не совпавший ни с чем, даёт обход из одной страницы,
а не отказ. Это не недосмотр: «на этой странице таких ссылок нет» — обычный
исход, а не ошибка запроса.

Отдельно стоит помнить про требование «хотя бы одно из `follow` и `next`».
Оно действует, когда `pages` больше единицы: обход без правила движения — это
обычный Fetch, за который взяли бы надбавку, и такое лучше отклонить, чем молча
исполнить. При `pages: 1` идти всё равно некуда, и требование снимается — на
этом стоит пробный прогон одной страницы.

## `out_of_credits`: до старта, а не посередине

Баланс проверяется по `credits_max` — по полной стоимости заказанных
`pages`, — и решение принимается один раз, до запуска. Начать обход и
остановиться на середине означало бы оставить вас с куском каталога, за
который уже заплачено.

Если нужен обход больше, чем есть на балансе, уменьшайте `pages`: два обхода
по сто страниц стоят ровно столько же, сколько один из двухсот.

## `already_finished`: обычная гонка, а не поломка

Отмена срабатывает на границе страницы, и между вашей просьбой и её
исполнением проходит время. Если за это время задание успело закончиться само,
приходит `409`. Ничего чинить не нужно: перечитайте состояние и посмотрите
`status` — там `done`, `partial` или `failed`.

## `service_unavailable`: лучше отказать, чем потерять

Задание живёт в хранилище: оттуда его берёт исполнитель, туда пишется ход, там
оно ждёт перезапуска. Если хранилище не отвечает, задание не принимается
вовсе.

Принять обход, которого потом не найти по идентификатору, — худший из
возможных исходов: деньги списаны, страницы где-то собираются, а забрать их
нечем. Честный отказ восстанавливается повтором через минуту.

## Что ошибкой не считается

### Страница не взялась

    Чужой сайт время от времени отдаёт `502` или не отвечает. Такая страница
    попадает в `pages_failed`, в счёт не идёт, а обход **продолжается**:
    ронять девяносто девять страниц из-за одной незачем. Если не взялась часть
    — итоговый статус `partial`, подробности в `note`.

### Ссылка ведёт во внутреннюю сеть

    Каждый найденный адрес проверяется на принадлежность внутренней сети и
    молча пропускается, если ведёт внутрь. Это не ваша ошибка и не повод
    отказывать в задании.

### Страниц вышло меньше, чем заказано

    `pages` — потолок, а не обещание. Ссылки кончились на 47-й из 200 —
    статус `done`, `pages_done: 47`, счёт за 47. Ошибки здесь нет.

### Задание долго стоит в queued

    Обход уступает живым запросам. Пока в пуле браузеров нет запаса вкладок,
    задание ждёт — это штатное поведение, а не зависание.

### Разбор не удался на отдельной странице

    `parsed: null`, причина в `extract_note`, разбор из счёта этой страницы
    вычитается. Остальные страницы это не задевает — см.
    [Обход с разбором](https://docs.parreq.com/crawl-extract).

## Дальше

**[Общие ошибки](https://docs.parreq.com/errors)**
    Формат конверта и коды, общие для всего API.

**[Задания](https://docs.parreq.com/crawl-jobs)**
    Статусы, `note`, отмена и что значит `partial`.
