Error handling¶
Every error the SDK raises implements the marker interface
CleverCloud\Sdk\Exception\CleverCloudException (extends \Throwable). Catch
that to handle any SDK failure, then narrow as needed.
\Throwable
└── \RuntimeException
└── CleverCloudException (interface)
├── ApiException — HTTP 4xx / 5xx returned by Clever Cloud
│ ├── AuthException — 401 / 403
│ ├── NotFoundException — 404
│ ├── ValidationException — 400 / 422 with field-level errors
│ ├── RateLimitException — 429 (after retries exhausted)
│ └── ServerException — 5xx (after retries exhausted)
├── TransportException — PSR-18 network / TLS / DNS failure
├── ConfigurationException — invalid builder / RetryPolicy input
└── JsonException — invalid JSON body
ApiException¶
Base for every HTTP error the platform returns. It's concrete — raised verbatim for any 4xx that doesn't match a more specific subclass (e.g. 418, 409, etc.).
use CleverCloud\Sdk\Exception\ApiException;
try {
$client->applications->restart('app_xxx');
} catch (ApiException $e) {
$e->statusCode; // int — HTTP status
$e->errorCode; // ?string — `code` / `error_code` / `type` field from the body
$e->requestId; // ?string — `X-Request-Id` / `Sozu-Id` / `X-Sozu-Id`
$e->body; // array<string, mixed> — decoded JSON body (empty on no/invalid body)
$e->getMessage();
$e->getPrevious();
}
All five properties are public readonly. Constructor signature verified in
src/Exception/ApiException.php.
AuthException — 401 / 403¶
Same shape as ApiException. Raised when Clever Cloud rejects your
credentials or your token lacks the required scope.
NotFoundException — 404¶
Same shape as ApiException. Raised when the targeted resource doesn't
exist (or you're not allowed to see it).
ValidationException — 400 / 422¶
Adds a $errors field on top of the base properties:
use CleverCloud\Sdk\Exception\ValidationException;
try {
$client->applications->create(['name' => '']);
} catch (ValidationException $e) {
$e->errors; // array<string, list<string>> — field name => list of messages
foreach ($e->errors as $field => $messages) {
printf("%s: %s\n", $field, implode(', ', $messages));
}
}
The map is filled from the response body's errors or violations field
(in that order). String values get wrapped in a single-element list so the
shape is always field => list<string>.
See src/Exception/ValidationException.php.
RateLimitException — 429¶
Adds $retryAfterSeconds:
use CleverCloud\Sdk\Exception\RateLimitException;
try {
$client->applications->list();
} catch (RateLimitException $e) {
$e->retryAfterSeconds; // ?int — server-provided Retry-After in seconds
}
This exception is only raised when retries have been exhausted. While
attempts remain, the SDK transparently sleeps for Retry-After (or the
RetryPolicy fallback) and retries.
See src/Exception/RateLimitException.php.
ServerException — 5xx¶
Same shape as ApiException. Raised when the platform returns a 5xx and
retries have been exhausted (RetryPolicy::$maxAttempts).
TransportException¶
Wraps a Psr\Http\Client\ClientExceptionInterface (DNS failure, TLS handshake
error, connection reset, etc.). Raised after retries on transport errors
are exhausted.
use CleverCloud\Sdk\Exception\TransportException;
try {
$client->self->get();
} catch (TransportException $e) {
$e->getMessage(); // "Transport error after N attempt(s): <psr18 message>"
$e->getPrevious(); // the underlying ClientExceptionInterface
}
ConfigurationException¶
Raised eagerly when:
ClientBuilder::build()is called without credentials.RetryPolicy::__construct()receives invalid values (negative delays,multiplier < 1.0,maxAttempts < 1).
JsonException¶
Raised by JsonCodec on malformed JSON in API responses, or by
AbstractResource::mapTo() when AutoMapper returns null for a payload.
How status codes map to exception classes¶
Verified against HttpClient::mapError() in
src/Http/HttpClient.php:
| HTTP status | Exception |
|---|---|
| 401, 403 | AuthException |
| 404 | NotFoundException |
| 400, 422 | ValidationException |
| 429 | RateLimitException (after retries exhausted) |
| ≥ 500 | ServerException (after retries exhausted) |
| other 4xx | ApiException |
Body / message / request ID extraction¶
Verified against HttpClient::extractMessage(), extractErrorCode(),
extractRequestId():
- Message: first non-empty of
body.message,body.error,body.error_description,body.detail, thenResponse::getReasonPhrase(), finally"HTTP {code}". - Error code: first non-empty of
body.code,body.error_code,body.type. - Request ID: first non-empty of headers
X-Request-Id,Sozu-Id,X-Sozu-Id. - Body: decoded as
array<string, mixed>when the JSON is an object;{_raw: <original>}when it's a list or when JSON decoding fails.