Catch the errors of the SDK

PHP

What an error of the SDK carries, how to catch it by type and branch on its code, and which errors say that a run may have cost money.

5 min read

A call of the SDK that the API refuses, or that gets no answer, raises an error. Every error of the SDK has one root type and keeps the code of the API, so one catch takes them all, and the code tells them apart.

What an error carries

WhatPHPNode.js
The code of the APIerrorCodecode
The HTTP statusstatusCodestatus
A sentence for a persongetMessage()message
More, where the code carries itdetailsdetails
The id of the requestrequestIdrequestId
How long a refused 429 or 503 asked to wait, in secondsretryAfterSecondsretryAfterSeconds
  • Branch on the code. The message is for a person, and its wording may change.

  • A connection error and the timeout of the client have neither a code nor a status: the answer of the API was not read. The errors the SDK raises itself, before it sends a request or over an answer it cannot read, have a code of the SDK's own — Error types lists them.

  • The id of the request is what support asks for. It is the X-Request-Id header of the answer.

Catch by type, branch on the code

PHP
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 the narrow type before the wide one: a catch of the root also takes a refused spend.

  • The refusals of a spend are one family — When spend is refused says what to do about each.

  • Error types names the type each code becomes.

  • An error of your own code is not an error of the SDK. Let it go on.

The SDK sends a request again by itself only after no answer, a 429 or a 503 — Retries and timeouts. What it raises is the error of the last attempt. Any other answer of the API that is an error is raised at once. A wait reads the status again itself after a failed read, as Async runs and waiting describes.

Whether the run cost money

The money of a run is the money of the model calls its flow made. The type says whether money may have been spent, and so whether the call may be sent again.

  • Refused before anything ran — SpendRefusedException, PolicyDeniedException, AuthenticationException, InvalidRequestException, CustomerNotFoundException, RateLimitException, and their twins in Node.js. Nothing ran and nothing was held. The same request is refused again until what refused it has changed: a budget starts its next window, the customer is topped up, the key gets its scope. A rate limit passes after the pause it asks for, and concurrency_limit_exceeded once a running call ends.

  • A key that is taken — IdempotencyConflictException, IdempotencyConflictError. Under idempotency_in_progress the first request under the key has not finished: send the call again later under the same key. Under idempotency_conflict the key belongs to another request: send yours under a new key.

  • Slen itself — ServiceUnavailableException, ServiceUnavailableError. The request was not carried out, and no provider was called. Send it again later under the same key.

  • No answer — TimeoutException, ConnectionException and their twins in Node.js, without a code. It is not known whether Slen did the work. The SDK has already sent the request again under the same key; send it again only under that key, and Slen does the work once.

  • An unknown outcome — OperationUncertainException, OperationUncertainError. A provider was called and its answer was lost, so the call may have been paid for. Do not send the run again under a new key — When the outcome is unknown.

  • The provider — ProviderException, ProviderError. With the code provider_error, provider_unavailable, provider_rate_limit, provider_timeout, provider_not_configured or pool_exhausted, the model call that failed gave its money back. With any other code, or with none, the error does not say how far the run got: treat it as an unknown outcome.

  • The flow — FlowRuntimeException, FlowRuntimeError, and any error out of operations.execute() once the walk of the flow had begun: a refusal or a failure at a later node, ledger_unavailable, execution_timeout and background_hold_expired. The model calls made before the error were paid for. A repeat under a new key pays for them again; under the same key Slen answers with the execution as it ended.

  • An unreadable answer — InvalidResponseException, InvalidResponseError. The API answered with a 2xx that cannot be read, so the call may have gone through. Send it again only under the same key.

A movement of credits that got no answer moves the credits once under its key: send it again under the same key.

A refusal read later

operations.execute() raises the error its request is answered with. The execution the request opened is closed, and it keeps the code. Read later, it is not an error: a repeat of the request under its key, a wait and a status read hand back the execution, and its status and error_code say how it ended.

PHP
$again = $slen->operations()->execute('generate_email', $input, new ExecuteOptions(idempotencyKey: 'order-42:email'));

if ($again->isRejected()) {
    echo 'rejected: ', $again->errorCode, PHP_EOL;
}

isRejected() is true when the status is rejected, and errorCode holds the code.

A flow can also refuse on purpose. A Reject Request node answers with the execution and the status rejected, never with an error. So code that reacts to a refusal handles both: the catch and the status of what came back. Quickstart lists the statuses of an execution.

Where to go next