Ловите ошибки SDK
PHPЧто несёт ошибка SDK, как ловить её по типу и ветвиться по коду и какие ошибки говорят, что запуск мог стоить денег.
5 мин чтения
Вызов SDK, который API отклоняет или который остаётся без ответа, выбрасывает ошибку. У всех ошибок SDK один корневой тип, и каждая хранит код API, поэтому один catch ловит их все, а код их различает.
Что несёт ошибка
| Что | PHP | Node.js |
|---|---|---|
| Код API | errorCode | code |
| HTTP-статус | statusCode | status |
| Фраза для человека | getMessage() | message |
| Подробности, где код их несёт | details | details |
| Идентификатор запроса | requestId | requestId |
Сколько секунд просил подождать отклонённый 429 или 503 | retryAfterSeconds | retryAfterSeconds |
Ветвитесь по коду. Сообщение — для человека, и его формулировка может меняться.
У ошибки соединения и у таймаута клиента нет ни кода, ни статуса: ответ API не прочитан. У ошибок, которые SDK выбрасывает сам — до отправки запроса или на ответе, который он не может прочитать, — свой код SDK; их перечисляет статья Типы ошибок.
Идентификатор запроса — то, что спросит поддержка. Это заголовок
X-Request-Idответа.
Ловить по типу, ветвиться по коду
use Slen\Sdk\Exceptions\OperationUncertainException;
use Slen\Sdk\Exceptions\SlenException;
use Slen\Sdk\Exceptions\SpendRefusedException;
use Slen\Sdk\Modules\Operations\Options\ExecuteOptions;
$input = ['messages' => [['role' => 'user', 'content' => 'Write a short follow-up email.']]];
try {
$execution = $slen->operations()->execute('generate_email', $input, new ExecuteOptions(idempotencyKey: 'order-42:email'));
echo $execution->status->value, PHP_EOL;
} catch (SpendRefusedException $error) {
// The spend was refused, and the code says what refused it.
echo 'refused: ', $error->errorCode, PHP_EOL;
} catch (OperationUncertainException $error) {
// A provider was called and its answer was lost: the call may have been paid for.
echo 'uncertain, request ', $error->requestId, PHP_EOL;
} catch (SlenException $error) {
echo $error->statusCode, ' ', $error->errorCode, ': ', $error->getMessage(), PHP_EOL;
}Ловите узкий тип раньше широкого:
catchкорневого типа ловит и отклонённую трату.Отказы в трате — одно семейство; что делать с каждым, говорит статья Когда трата отклонена.
Какой тип получает каждый код, называет статья Типы ошибок.
Ошибка вашего собственного кода — не ошибка SDK. Пропустите её дальше.
SDK сам отправляет запрос ещё раз, только когда ответа не было, после 429 или 503 — Повторы и таймауты. Выбрасывает он ошибку последней попытки. Любой другой ответ API, который является ошибкой, выбрасывается сразу. Ожидание само читает статус ещё раз после неудачного чтения — как описывает статья Асинхронные запуски и ожидание.
Стоил ли запуск денег
Деньги запуска — это деньги вызовов моделей, которые сделал его флоу. Тип говорит, могли ли быть потрачены деньги и, значит, можно ли отправить вызов ещё раз.
Отклонено, прежде чем что-то выполнилось —
SpendRefusedException,PolicyDeniedException,AuthenticationException,InvalidRequestException,CustomerNotFoundException,RateLimitExceptionи их двойники в Node.js. Ничего не выполнилось и ничего не зарезервировано. Тот же запрос отклоняется снова, пока не изменится то, что его отклонило: бюджет начнёт следующее окно, клиента пополнят, ключ получит разрешение. Лимит запросов пропускает после паузы, о которой просит, аconcurrency_limit_exceeded— когда идущий вызов закончится.Занятый ключ —
IdempotencyConflictException,IdempotencyConflictError. Приidempotency_in_progressпервый запрос под этим ключом ещё не закончился: отправьте вызов ещё раз позже под тем же ключом. Приidempotency_conflictключ принадлежит другому запросу: отправьте свой под новым ключом.Сам Slen —
ServiceUnavailableException,ServiceUnavailableError. Запрос не выполнен, и провайдер не вызывался. Отправьте его ещё раз позже под тем же ключом.Нет ответа —
TimeoutException,ConnectionExceptionи их двойники в Node.js, без кода. Неизвестно, сделал ли Slen работу. SDK уже отправил запрос ещё раз под тем же ключом; отправляйте его ещё раз только под этим ключом, и Slen сделает работу один раз.Неизвестный исход —
OperationUncertainException,OperationUncertainError. Провайдер был вызван, а его ответ потерян, поэтому за вызов могли заплатить. Не отправляйте запуск ещё раз под новым ключом — Когда исход неизвестен.Провайдер —
ProviderException,ProviderError. С кодомprovider_error,provider_unavailable,provider_rate_limit,provider_timeout,provider_not_configuredилиpool_exhaustedвызов модели, который не удался, вернул свои деньги. С любым другим кодом или без кода ошибка не говорит, как далеко зашёл запуск: считайте это неизвестным исходом.Флоу —
FlowRuntimeException,FlowRuntimeErrorи любая ошибка изoperations.execute()после того, как флоу начал выполняться: отказ или сбой на более позднем узле,ledger_unavailable,execution_timeoutиbackground_hold_expired. Вызовы моделей, сделанные до ошибки, оплачены. Повтор под новым ключом платит за них снова; под тем же ключом Slen отвечает выполнением таким, каким оно закончилось.Нечитаемый ответ —
InvalidResponseException,InvalidResponseError. API ответил2xx, который нельзя прочитать, поэтому вызов мог пройти. Отправляйте его ещё раз только под тем же ключом.
Движение кредитов, оставшееся без ответа, под своим ключом проходит один раз: отправьте его ещё раз под тем же ключом.
Отказ, прочитанный позже
operations.execute() выбрасывает ошибку, которой ответили на его запрос. Выполнение, которое открыл запрос, закрыто и хранит код. Если прочитать его позже, это уже не ошибка: повтор запроса под его ключом, ожидание и чтение статуса возвращают выполнение, а его статус и error_code говорят, чем оно закончилось.
$again = $slen->operations()->execute('generate_email', $input, new ExecuteOptions(idempotencyKey: 'order-42:email'));
if ($again->isRejected()) {
echo 'rejected: ', $again->errorCode, PHP_EOL;
}isRejected() возвращает true, когда статус — rejected, а errorCode хранит код.
Флоу может отказать и намеренно. Узел Отклонить запрос отвечает выполнением со статусом rejected, а не ошибкой. Поэтому код, который реагирует на отказ, обрабатывает и то и другое: catch и статус того, что вернулось. Статусы выполнения перечисляет статья Быстрый старт.
Что читать дальше
Типы ошибок — какой тип получает каждый код API и какие ошибки SDK выбрасывает сам.
Когда трата отклонена — бюджет, лимит или кредиты клиента сказали «нет».
Коды ошибок — что значит каждый код и что с ним делать.
Тестирование интеграции — поставьте ошибку в очередь тестового дублёра и проверьте, как её обрабатывает ваш код.