Skip to main content

Error Handling

Uncaught Errors

Any error parsing a JSON-RPC request or any error that occurs executing a function will be wrapped in a JSON-RPC Error Object.

By default, the error returned will be a generic server error. The following:

from openrpc import RPCApp

rpc = RPCApp()


@rpc.method()
def divide(a: int, b: int) -> float:
return a / b


req = '{"id": 1, "method": "divide", "params": {"a": 2, "b": 0}, "jsonrpc": "2.0"}'
print(await rpc.process(req))

Produces this error response:

{
"id": 1,
"error": {
"code": -32000,
"message": "Server error"
},
"jsonrpc": "2.0"
}

Debug

In order to include stack trace in the error response set debug=True for the RPCApp.

rpc = RPCApp()

Now the error response will contain error details in the data.

{
"id": 1,
"error": {
"code": -32000,
"message": "Server error",
"data": "ZeroDivisionError: division by zero\n File \"/home/matthew/Projects/openrpc-app/app.py\", line 8, in divide\n return a / b\nZeroDivisionError: division by zero\n"
},
"jsonrpc": "2.0"
}

Custom Errors

You will likely want errors that preserve the error code and message.

For that, create and error class that extends OpenRPCError.

from openrpc import OpenRPCError, RPCApp

rpc = RPCApp()


class CustomError(OpenRPCError):
"""Error that will preserve custom code, message, and data."""

def __init__(self, a: int, b: int, *args: object) -> None:
"""Instantiate custom error."""
self.message = "Custom RPC error."
self.code = -32002
self.data = {"a": a, "b": b}
super().__init__(self.code, self.message, self.data, *args)


@rpc.method()
def divide(a: int, b: int) -> float:
"""Divide two numbers."""
try:
return a / b
except ZeroDivisionError as e:
raise CustomError(a, b) from e


req = '{"id": 1, "method": "divide", "params": {"a": 2, "b": 0}, "jsonrpc": "2.0"}'
print(await rpc.process(req))

Now error details are preserved.

{
"id": 1,
"error": {
"code": -32002,
"message": "Custom RPC error.",
"data": {
"a": 2,
"b": 0
}
},
"jsonrpc": "2.0"
}