rpc.error

rpc.error

Event name

rpc.error

Constant

RpcEvent::ERROR

Class

RpcErrorEvent

Properties

RpcRequest $rpcRequest, Throwable $exception, RpcError $rpcError

An error during request execution. $rpcError is the error object the response will be built from; it can be replaced.

When it is dispatched

  • Parameter validation and #[RPC\Assertions] errors.
  • Parameter conversion (DTO) errors.
  • An exception or TypeError from the method.
  • A request marked with an error at earlier stages (setError()).
  • In the console, inline batch and via RpcExecutor — also for any other processing exception.
Inconsistency

In a single HTTP request, some errors (invalid JSON, token, unknown method, jsonrpc version, batch) do not go through rpc.error and are not logged by the bundle. Details — in Open issues.

Built-in listeners

Priority Listener What it does
1000 RpcErrorListener For ConstraintsImposedException, adds the list of violations to data
0 LoggerListener Logs the error (method, parameters, data)
-1000 RpcServerListener::onRpcError Builds the error response from $event->rpcError (rpc.pre_response) and stops propagation

What can be changed

$event->rpcError is the supported way to change the error the client receives:

$event->rpcError = new RpcError($code, $message, $data);
  • Priority between 1000 and 0 — your version goes both to the log and to the client; between 0 and -1000 — the log sees the original.
  • $rpcError is created from $exception in the constructor; assigning a new $exception does not recompute it.
  • Listeners below -1000 are not called. Do not stop propagation above -1000 — the error response will not be built.

Usage examples

Hiding internal details

#[AsEventListener(RpcEvent::ERROR, priority: 500)]
final class HideInternalErrorsListener
{
    public function __construct(#[Autowire('%kernel.environment%')] private string $env) {}

    public function __invoke(RpcErrorEvent $event): void
    {
        if ($this->env === 'prod' && $event->exception instanceof RpcRuntimeException) {
            $event->rpcError = new RpcError(-32500, 'Internal server error', []);
        }
    }
}

Sending to a monitoring system

#[AsEventListener(RpcEvent::ERROR, priority: 100)]
final class SentryListener
{
    public function __invoke(RpcErrorEvent $event): void
    {
        \Sentry\withScope(function (\Sentry\State\Scope $scope) use ($event): void {
            $scope->setTag('rpc.method', $event->rpcRequest->getMethod());
            \Sentry\captureException($event->exception);
        });
    }
}

Other ideas

  • converting the application's domain exceptions into custom error codes;
  • an error counter by method and code;
  • adding a request_id to the error data for support.