Error handling
Format
Every error of a REST call or document is returned as an RFC 9457 (problem details) document with the HTTP status of the error:
{
"_error_object_standard": "RFC 9457",
"status": 400,
"type": "about:blank",
"instance": "/rest/catalog/items/42",
"title": "Ufo\\RestBundle\\Exceptions\\RestBadRequestException",
"detail": "Invalid Data for call method: catalog.update",
"detail-data": {
"title": ["This value is too short. It should have 3 characters or more."]
}
}
| Field | Value |
|---|---|
status |
the HTTP status, the same as the response status |
type |
always about:blank |
instance |
the request path |
title |
the REST exception class |
detail |
the error message |
detail-data |
only if the RPC error contains an array of data: per-parameter messages for validation and type errors |
Stack traces are not included in the document.
How the status is chosen
The RPC core turns any method exception into an RPC error (the rpc.error event): exceptions from the ufo-tech/rpc-exceptions package keep their class and code, an argument TypeError becomes a parameter error, other exceptions are wrapped (Error handling in JsonRpcBundle). The REST layer recreates the exception from the RPC error and converts it with the RestErrorNormalizer class:
| RPC exception | REST exception | Status |
|---|---|---|
RpcJsonParseException |
RestBadRequestException |
400 |
RpcMethodNotFoundExceptionRpc |
RestResourceNotFoundException |
404 |
ConstraintsImposedException |
RestValidationException |
422 |
RpcBadParamException (parameter validation and type errors) |
RestBadRequestException |
400 |
RpcBadRequestException |
RestBadRequestException |
400 |
RpcInternalException, RpcRuntimeException |
RestInternalServerErrorException |
500 |
RpcLogicException |
RestBadRequestException |
400 |
RpcTokenNotSentException, RpcInvalidTokenException |
RestUnauthorizedException |
401 |
RpcDataNotFoundException |
RestResourceNotFoundException |
404 |
CustomApplicationException |
RestApplicationException |
the exception code if it is an HTTP error code, otherwise 400 |
| any other | RestBadRequestException |
the exception code if it is an HTTP error code, otherwise 400 |
The table is checked top to bottom with instanceof, so subclasses come before their parent classes. An HTTP error code is a code from the RestErrorNormalizer::REST_ERROR_CODES list (standard 4xx and 5xx codes). If an exception from any row of the table has such a code, the status is taken from it.
The 404 row for RpcMethodNotFoundExceptionRpc, the 422 row for ConstraintsImposedException and keeping the HTTP code of an application error are available since version 1.3. In 1.0–1.2 these cases returned 400. See Fixed issues.
REST layer errors
| Situation | Exception | Status |
|---|---|---|
| no REST route for the path, unknown version, a method of another group | RestResourceNotFoundException |
404 |
| the HTTP method is not allowed for the route | RestResourceNotAllowedException |
405 |
| invalid or scalar JSON body | RestBadRequestException |
400 |
| token not sent or invalid | RestUnauthorizedException |
401 |
Returning a specific status from a method
Throw an RPC exception from the table or an exception with an HTTP code:
use Ufo\RpcError\CustomApplicationException;
use Ufo\RpcError\RpcDataNotFoundException;
/**
* @throws RpcDataNotFoundException
* @throws CustomApplicationException
*/
#[Route('/items/{id}', name: 'item', methods: ['GET'])]
public function item(int $id): array
{
$item = $this->repository->find($id) ?? throw new RpcDataNotFoundException('Item is not found'); // 404
if ($item->isClosed()) {
throw new CustomApplicationException('Item is closed', 409); // 409
}
return $item->toArray();
}
Via JSON-RPC the same exceptions produce ordinary RPC errors, so one method serves both protocols. The @throws tags add these statuses to the OpenAPI documentation.
Notes:
- An exception with code
0becomes an internal RPC error and returns500. - RPC core validation errors (
#[RPC\Assertions], parameter types) return400withdetail-data.422applies only to aConstraintsImposedExceptionthat reaches the REST layer directly. - An unknown method or path gives
404, but a wrong HTTP method for an existing path gives405.