Exceptions
Everything the SDK throws extends S5wException. The exception’s class says what happened; the
message text is meant for logs. Handling them in practice — in the
Error handling guide.
Hierarchy
S5wException RuntimeException; the root of every SDK error
├── TransportException the server did not answer: DNS, refused connection, timeout
├── ResponseException an answer came, but not in S5W's format
├── ApiException the server answered with an error status
│ ├── UnauthorizedException 401 no key, or the server does not know it
│ ├── ForbiddenException 403 the key is disabled, expired, or BASIC where FULL is needed
│ ├── NotFoundException 404 no such file, folder or session
│ ├── ConflictException 409 the name is taken; a chunk does not continue the upload
│ ├── PayloadTooLargeException 413 the quota is full; a chunk is too large
│ ├── ValidationException 422 the parameters failed validation
│ └── ServerException 5xx a failure on the server's side
├── UploadInterruptedException a chunked upload stopped, the session can be continued
└── UploadUnconfirmedException assembling the file was requested but not confirmedS5wException
The root of the hierarchy, a subclass of RuntimeException. Catch it and you catch any SDK error.
It is also thrown directly — for errors the SDK finds before any request: invalid configuration,
a file that cannot be read from disk, a file that changed during the upload, a file whose size
does not match the session on resume().
Message
Messages look like S5W {path}: {status} {server text}:
S5W v1/files/7CKEKbaV8AN6qTs1: 404 File does not exist
S5W v1/folders: 409 Folder "avatars" already exists
S5W v1/files: 422 Folder "no-such" does not exist
S5W v1/check: simulated: connection refusedThe path is shown decoded — non-Latin folder names read as they are.
ApiException
The server answered with an error status. The body is { code, message, errors? }. For the
statuses in the table below a subclass is thrown; for the others (400, for example) —
ApiException itself.
| Status | Class | Typical causes |
|---|---|---|
| 400 | ApiException |
an empty chunk, or a chunk under 4 MiB that is not the last |
| 401 | UnauthorizedException |
no key, or an unknown one |
| 403 | ForbiddenException |
the key is disabled or expired; a BASIC key for folders(), files(), uploads() |
| 404 | NotFoundException |
no such file, folder, session; a malformed file id (no request) |
| 409 | ConflictException |
a folder or file name is taken; the chunk offset does not match; complete() before every byte arrived |
| 413 | PayloadTooLargeException |
the bucket’s quota is full; a chunk over 16 MiB or past the declared size |
| 422 | ValidationException |
a field failed validation; no such folder; image options for a non-image; a hash mismatch |
| 5xx | ServerException |
a server error; 507 — the server’s disk has no room |
Methods
status(): int — the HTTP status of the answer.
errors(): array<string, list<string>> — per-field errors; filled only for a 422 with field
validation errors.
method(): string — the request’s HTTP method, e.g. "PUT".
path(): string — the request path relative to the server address, e.g. "v1/files/7CKEKbaV8AN6qTs1".
catch (ApiException $e) {
$e->status(); // 409
$e->method(); // "POST"
$e->path(); // "v1/folders"
}ValidationException
An extra method, errorsOf(string $field): list<string> — the errors of one field.
catch (ValidationException $e) {
$e->errors(); // ["name" => ["must not be blank", "size must be between 1 and 100"]]
$e->errorsOf('name'); // ["must not be blank", "size must be between 1 and 100"]
$e->errorsOf('other'); // []
}If the error is about the whole request rather than a field (no such folder, a hash mismatch),
errors() is empty and the cause is in the message.
TransportException
The request got no HTTP answer: the server was not found, the connection was refused, the timeout expired. The one kind of failure worth retrying as it is.
Methods
isTimeout(): bool — true if the cause is an expired timeout rather than a refused connection.
The HTTP client’s original exception is available through getPrevious().
ResponseException
An answer came, but it is not an S5W answer: not JSON, a field missing, a field of the wrong type. Usually the address points at something other than S5W (a proxy error page, say), or the server and SDK versions disagree. The message names the field:
S5W v1/check: the answer is not JSON
S5W v1/check: bytes.quota must be int, got stringUploadInterruptedException
A chunked upload stopped on a network or server failure after every attempt
(Uploads::ATTEMPTS) ran out. The session is kept on
the server — it can be continued within 24 hours of the last chunk received.
Thrown by uploads()->send(), sendContent(), resume(), and by files()->upload() and
uploadContent() for files over the chunked-upload threshold.
Methods
sessionId(): string — the session id; pass it to uploads()->resume().
offset(): int — how many bytes, as far as the SDK knows, are already on the server.
size(): int — the full file size.
The cause is in getPrevious(): TransportException, ServerException or ConflictException.
catch (UploadInterruptedException $e) {
$file = $s5w->uploads()->resume($e->sessionId(), $path);
}UploadUnconfirmedException
Every byte was sent and assembling the file was requested, but no answer came (a broken connection, a gateway error), and the session is no longer on the server. The file may have been created — then its id is unknown; or it may not. The SDK does not try to guess: a name search could find someone else’s file.
Methods
sessionId(): string — the id of the session that is gone.
name(): string — the name the upload started with. The stored name may differ — e.g.
name (1).ext if the name was taken.
catch (UploadUnconfirmedException $e) {
$logger->error("Upload of {$e->name()} not confirmed — check the folder by hand");
}What methods throw
| Method | Characteristic exceptions |
|---|---|
S5w::check() |
UnauthorizedException, ForbiddenException |
Folders::create(), rename(), update() |
ConflictException, ValidationException, NotFoundException |
Folders::get(), delete() |
NotFoundException |
Files::get(), delete() |
NotFoundException |
Files::list() |
ValidationException (no such folder) |
Files::rename(), move(), update() |
NotFoundException, ValidationException, ConflictException |
Files::upload(), uploadContent() |
ValidationException, PayloadTooLargeException; for large files — as send() |
Uploads::start() |
ValidationException, PayloadTooLargeException, ServerException (507) |
Uploads::append() |
ConflictException, PayloadTooLargeException, ApiException (400), NotFoundException |
Uploads::complete() |
ConflictException, ValidationException |
Uploads::send(), sendContent() |
UploadInterruptedException, UploadUnconfirmedException, ApiException |
Uploads::resume() |
as send(), plus NotFoundException and S5wException (a different file) |
Any method that talks to the server may also throw TransportException, ResponseException,
UnauthorizedException and ForbiddenException.