Для AI-агентов: markdown этой страницы — /docs-content/errors/retry-safety.md индекс документации — /llms.txt
Что безопасно повторять
HTTP-статус и слово retry сами по себе не отвечают на главный вопрос: не
создаст ли повтор записи дубль. Для решения нужны две отдельные характеристики —
успел ли целевой вызов метода уйти в Битрикс24 и является исходный запрос чтением
или записью.
Retry-After указывает, когда допустима следующая попытка с точки зрения нагрузки.
Он не доказывает, что предыдущая запись не применилась.
Термины
Целевой вызов
Целевой вызов — бизнес-метод Битрикс24, который вы хотели выполнить, например
crm.deal.get или crm.deal.add. Служебное обновление OAuth-токена целевым
вызовом не считается.
Для BH_APP_TIMEOUT исход запроса неизвестен. Код возникает, когда агент не получил заголовки за 14 секунд после отправки запроса или исчерпал общий 20-секундный бюджет чтения небольшого ответа; когда шлюз не получил первый кадр за 30 секунд или 15 секунд не видел продолжения потока до фиксации ответа; а также когда само приложение вернуло 504 без Content-Type — эта форма неотличима от отказа агента. Запрос мог дойти до приложения и выполнить вызов Битрикс24. По одному коду нельзя определить, произошло ли это.
Чтение и запись
Чтение не меняет данные: его повтор обычно безопасен с точки зрения дублей.
Запись создаёт, изменяет, удаляет или отправляет данные, поэтому для неё нужен
отдельный вывод. Определяйте тип операции по смыслу метода, а не по HTTP-глаголу:
POST может выполнять поиск, а методы *.add, *.update, *.delete и *.send
меняют состояние.
Неизвестный исход
«Неизвестно» означает, что по коду нельзя доказать, успел ли целевой вызов создать эффект. Отсутствие успешного ответа не равно отсутствию эффекта.
Правило принятия решения
- Повтор безопасен — дождитесь указанного условия и повторяйте с ограничением числа попыток.
- Сначала перечитать — найдите эффект операции по устойчивому бизнес-ключу или идентификатору. Повторяйте запись только если можете доказать, что эффекта нет.
- Не повторять автоматически — исправьте причину либо передайте случай человеку. После изменения входных данных это новая осознанная попытка, а не retry того же запроса.
Если надёжно проверить эффект записи невозможно, «сначала перечитать» означает
«не повторять автоматически». X-Request-Id нужен для диагностики и не является
ключом идемпотентности.
Матрица кодов
| Код | Целевой вызов дошёл до Битрикс24 | Чтение | Запись | Условие и пояснение |
|---|---|---|---|---|
КодQUEUE_OVERFLOW |
Целевой вызов дошёл до Битрикс24Нет | ЧтениеПовтор безопасен | ЗаписьПовтор безопасен | Условие и пояснениеВыдержать Retry-After, снизить параллелизм, использовать backoff со случайной добавкой. Очередь отказала до запуска вызова. |
КодQUEUE_TIMEOUT |
Целевой вызов дошёл до Битрикс24Нет | ЧтениеПовтор безопасен | ЗаписьПовтор безопасен | Условие и пояснениеВыдержать Retry-After. Вызов удалён из очереди до начала исполнения. |
КодRATE_LIMITED |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьПовтор безопасен | Условие и пояснениеВыдержать Retry-After. Код объединяет локальные ограничения до вызова и отказ Битрикс24 по частоте без применения операции. |
КодCALLER_RATE_LIMITED |
Целевой вызов дошёл до Битрикс24Нет | ЧтениеПовтор безопасен | ЗаписьПовтор безопасен | Условие и пояснениеВыдержать Retry-After — до конца минутного окна тот же отказ повторится. Платформа отказала до запуска операции. |
КодOPERATION_TIME_LIMIT |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеСтрого выдержать Retry-After. Первый отказ приходит от уже вызванного метода Битрикс24, последующие могут возникать локально до отправки. |
КодTIMEOUT_QUARANTINE |
Целевой вызов дошёл до Битрикс24Нет | ЧтениеПовтор безопасен | ЗаписьПовтор безопасен | Условие и пояснениеНе повторять раньше полного Retry-After: досрочные запросы мешают восстановлению. Текущий вызов остановлен локально до отправки. |
КодBITRIX_TIMEOUT |
Целевой вызов дошёл до Битрикс24Да | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеДождаться рекомендуемой паузы. Платформа прекратила ждать ответ, но обработка записи в Битрикс24 могла продолжиться. |
КодBITRIX_ERROR |
Целевой вызов дошёл до Битрикс24Да | ЧтениеПовтор безопасен после исправления причины | ЗаписьНе повторять автоматически | Условие и пояснениеИсправить причину по b24Code, message и полям ошибки. Простая пауза не меняет результат; для сомнительной записи дополнительно проверить состояние. |
КодBITRIX_UNAVAILABLE |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеИспользовать ограниченный экспоненциальный backoff. Код покрывает upstream 5xx и сетевые сбои; отсутствие ответа не доказывает отсутствие эффекта. |
КодPOOL_EXHAUSTED |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеВыдержать Retry-After. Общий обработчик ошибки БД может сработать на разных этапах обработки запроса. |
КодDB_TRANSIENT |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеВыдержать Retry-After. Откат локальной транзакции БД не откатывает уже совершённый вызов Битрикс24. |
КодSERVICE_UNAVAILABLE |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеВыдержать Retry-After. Граница платформы не знает, успел ли backend завершить внешний эффект; в частности, 504 не останавливает upstream. |
КодTOKEN_REFRESH_FAILED |
Целевой вызов дошёл до Битрикс24Нет | ЧтениеПовтор безопасен после восстановления авторизации | ЗаписьПовтор безопасен после восстановления авторизации | Условие и пояснениеЦелевой метод не отправлен. Один backoff без ремонта авторизации бесполезен. |
КодERROR_LOOP_DETECTED |
Целевой вызов дошёл до Битрикс24Нет | ЧтениеПовтор безопасен после устранения причины | ЗаписьНе повторять автоматически | Условие и пояснениеТекущий вызов остановлен circuit breaker до отправки. Сначала исправить стабильную причину; редкие probe-вызовы могут уйти дальше. |
КодBH_APP_STARTING |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеКадр от приложения не пришёл вовсе, либо ответил сам агент, а не приложение — оба случая говорят об отсутствии кадра от приложения, но не доказывают, что код приложения его не увидел: соединение могло оборваться уже во время начатой обработки. Подождать Retry-After. |
КодBH_APP_TIMEOUT |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеИсточник: окно агента 14 секунд до заголовков или 20 секунд до полного небольшого ответа, окно шлюза 30 секунд до первого кадра или 15 секунд тишины до фиксации, либо ответ приложения 504 без Content-Type. Запрос мог выполниться. Retry-After нет: пауза не делает повтор безопасным. |
КодBH_APP_CONNECTION_LOST |
Целевой вызов дошёл до Битрикс24Неизвестно | ЧтениеПовтор безопасен | ЗаписьСначала перечитать | Условие и пояснениеАгент подключился к приложению, но соединение оборвалось до ответа либо тело ответа не дочиталось. Запрос мог выполниться. Retry-After нет: пауза не делает повтор безопасным. |
КодAGENT_WRITE_ALREADY_EXECUTED |
Целевой вызов дошёл до Битрикс24Нет | Чтение— | ЗаписьСначала перечитать | Условие и пояснениеПриходит только ключу AI-агента на повтор DELETE по тому же адресу в течение 30 секунд, на чтение не приходит. Если в error.firstStatus код успеха, первое удаление выполнено — считайте операцию сделанной. null значит, что оно ещё выполняется: перечитайте запись. |
КодLARGE_BODY_BACKEND_BUSY |
Целевой вызов дошёл до Битрикс24Нет | ЧтениеПовтор безопасен | ЗаписьПовтор безопасен | Условие и пояснениеВыдержать Retry-After. Admission-гейт отказал до route handler и целевого вызова. |
Почему некоторые коды неоднозначны
`RATE_LIMITED`
Доставка неизвестна, потому что один код используют и локальные ограничения, и Битрикс24. Повтор записи после паузы всё же безопасен: известные источники либо не отправляют вызов, либо однозначно отказывают по частоте без применения операции. Это не означает, что любой ответ HTTP 429 всегда возникает до отправки.
`OPERATION_TIME_LIMIT`
Первый отказ с этим кодом возвращает уже вызванный метод Битрикс24. После него Вайбкод запоминает паузу и может отбивать следующие запросы до отправки. Поэтому для записи применяется более консервативное действие — сначала проверить эффект.
`TIMEOUT_QUARANTINE`
Безопасен текущий запрос, остановленный локальной паузой. Операция, чей более ранний таймаут включил карантин, могла иметь неизвестный исход — к ней это обещание не относится.
`DB_TRANSIENT`
Откат транзакции в базе Вайбкод не является распределённым откатом Битрикс24. Если внешний вызов уже завершился, локальный rollback не отменит его эффект.
`BH_APP_STARTING`
Кадр от приложения не пришёл вовсе, либо ответил сам агент, а не приложение. Оба
случая говорят об отсутствии кадра от приложения, но не доказывают, что код
приложения его не увидел: соединение могло оборваться уже во время начатой
обработки. Поэтому запись требует сначала перечитать состояние, а не повторяется
вслепую — в отличие от BH_APP_TIMEOUT ниже, у этого кода есть Retry-After.
`BH_APP_TIMEOUT`
Ответ может возникнуть из-за ожидания заголовков агентом 14 секунд после отправки запроса, исчерпания его общего 20-секундного бюджета на небольшой буферизуемый ответ, ожидания первого кадра шлюзом 30 секунд или 15 секунд тишины потока до фиксации ответа. Ответ самого приложения 504 без Content-Type имеет ту же форму и тоже преобразуется в этот код. Ни один из этих источников не доказывает отсутствие побочного эффекта: приложение могло вызвать Битрикс24. Retry-After нет; перед повтором записи проверьте её результат.
`BH_APP_CONNECTION_LOST`
Агент версии 1.3.41 и новее подключился к приложению, а соединение оборвалось до ответа, либо заголовки пришли, а тело не дочиталось. Приложение работает, и запрос мог дойти до него и выполнить вызов Битрикс24. Retry-After нет. Перед повтором записи проверьте её результат.
Алгоритм для чтения
- Распознайте код ответа.
- Для ошибки авторизации, бизнес-ошибки или circuit breaker сначала устраните причину.
- Для временного отказа выдержите
Retry-After; если его нет, примените ограниченный экспоненциальный backoff со случайной добавкой. - Повторите чтение не более заданного клиентом числа раз.
- При устойчивом отказе остановитесь и передайте поддержке время и
X-Request-Id, если он был в ответе.
Алгоритм для записи
- Не используйте единый список retryable-кодов для чтения и записи.
- Если ячейка «Запись» разрешает повтор, дождитесь указанного условия и повторяйте с ограничением числа попыток.
- Если требуется перечитать состояние, найдите именно бизнес-эффект исходной операции по уникальному признаку.
- Если эффект уже есть, считайте исходную запись успешной и не повторяйте её.
- Если эффект достоверно отсутствует, выполните новую попытку.
- Если проверка ненадёжна, остановите автоматический повтор и разрешите случай вручную.
Обычный GET без уникального критерия не доказывает отсутствие дубля. Если метод имеет собственный серверный ключ идемпотентности, можно опираться на контракт этого метода; эта страница сама такой гарантии не вводит.
Пример классификатора
Классификатор принимает вид операции явно. Он не пытается вывести его из HTTP-
глагола и не реализует универсальный readBack: проверка эффекта зависит от
конкретной сущности.
const VERIFY_WRITE_STATE = new Set([
'OPERATION_TIME_LIMIT', 'BITRIX_TIMEOUT', 'BITRIX_UNAVAILABLE',
'POOL_EXHAUSTED', 'DB_TRANSIENT', 'SERVICE_UNAVAILABLE', 'BH_APP_STARTING',
'BH_APP_TIMEOUT', 'BH_APP_CONNECTION_LOST', 'AGENT_WRITE_ALREADY_EXECUTED',
]);
const FIX_BEFORE_RETRY = new Set([
'BITRIX_ERROR', 'TOKEN_REFRESH_FAILED', 'ERROR_LOOP_DETECTED',
]);
const SAFE_RETRY = new Set([
'QUEUE_OVERFLOW', 'QUEUE_TIMEOUT', 'RATE_LIMITED', 'CALLER_RATE_LIMITED',
'TIMEOUT_QUARANTINE', 'LARGE_BODY_BACKEND_BUSY',
]);
function classifyRetry(code, operationKind) {
if (FIX_BEFORE_RETRY.has(code)) return 'fix_before_retry';
if (operationKind === 'write' && VERIFY_WRITE_STATE.has(code)) {
return 'state_verification_required';
}
if (SAFE_RETRY.has(code) || operationKind === 'read') return 'retry_with_limit';
return 'state_verification_required';
}
Неизвестный классификатору код обрабатывайте консервативно: чтение можно повторить с ограниченным backoff, запись — только после проверки состояния. Ошибка сети без JSON-конверта платформы имеет тот же неизвестный исход для записи.
В batch решение принимается отдельно для каждого элемента по его коду и семантике подзапроса. HTTP 200 у общего конверта не означает успех всех элементов.