0
# Exceptions and Error Handling
1
2
Comprehensive error handling system with predefined JSON-RPC error types, custom exception support, and automatic error response generation. The library provides both JSON-RPC standard errors and Python exception integration.
3
4
## Capabilities
5
6
### JSON-RPC Error Objects
7
8
Core error object representing JSON-RPC errors with code, message, and optional data fields.
9
10
```python { .api }
11
class JSONRPCError:
12
serialize = staticmethod # json.dumps
13
deserialize = staticmethod # json.loads
14
15
def __init__(self, code: int = None, message: str = None, data = None):
16
"""
17
Create JSON-RPC error object.
18
19
Parameters:
20
- code: Error code (integer, -32768 to -32000 reserved)
21
- message: Brief error description (string)
22
- data: Additional error information (any type)
23
"""
24
25
@classmethod
26
def from_json(cls, json_str: str):
27
"""Create error from JSON string."""
28
29
# Properties
30
code: int
31
message: str
32
data # Any type
33
json: str # JSON representation
34
```
35
36
### Predefined Error Types
37
38
Standard JSON-RPC error types with predefined codes and messages.
39
40
```python { .api }
41
class JSONRPCParseError(JSONRPCError):
42
"""Invalid JSON was received by the server."""
43
CODE = -32700
44
MESSAGE = "Parse error"
45
46
class JSONRPCInvalidRequest(JSONRPCError):
47
"""The JSON sent is not a valid Request object."""
48
CODE = -32600
49
MESSAGE = "Invalid Request"
50
51
class JSONRPCMethodNotFound(JSONRPCError):
52
"""The method does not exist / is not available."""
53
CODE = -32601
54
MESSAGE = "Method not found"
55
56
class JSONRPCInvalidParams(JSONRPCError):
57
"""Invalid method parameter(s)."""
58
CODE = -32602
59
MESSAGE = "Invalid params"
60
61
class JSONRPCInternalError(JSONRPCError):
62
"""Internal JSON-RPC error."""
63
CODE = -32603
64
MESSAGE = "Internal error"
65
66
class JSONRPCServerError(JSONRPCError):
67
"""Reserved for implementation-defined server-errors."""
68
CODE = -32000
69
MESSAGE = "Server error"
70
```
71
72
### Python Exception Classes
73
74
Standard Python exceptions for different error conditions.
75
76
```python { .api }
77
class JSONRPCException(Exception):
78
"""Base JSON-RPC exception."""
79
pass
80
81
class JSONRPCInvalidRequestException(JSONRPCException):
82
"""Request is not valid."""
83
pass
84
85
class JSONRPCDispatchException(JSONRPCException):
86
"""
87
JSON-RPC dispatch exception for method implementations.
88
89
Should be thrown in dispatch methods to return custom errors.
90
"""
91
92
def __init__(self, code: int = None, message: str = None, data = None, *args, **kwargs):
93
"""
94
Create dispatch exception with error details.
95
96
Parameters:
97
- code: JSON-RPC error code
98
- message: Error message
99
- data: Additional error data
100
- args, kwargs: Standard exception arguments
101
"""
102
103
error: JSONRPCError # Associated error object
104
```
105
106
### Error Detection Utilities
107
108
Utility functions for detecting and validating parameter errors.
109
110
```python { .api }
111
def is_invalid_params(func, *args, **kwargs) -> bool:
112
"""
113
Check if function parameters are invalid.
114
115
Used internally to distinguish TypeError from invalid parameters
116
vs TypeError from within the function.
117
118
Parameters:
119
- func: Function to validate against
120
- args: Positional arguments
121
- kwargs: Keyword arguments
122
123
Returns:
124
True if parameters are invalid for the function
125
"""
126
```
127
128
## Usage Examples
129
130
### Basic Error Handling
131
132
```python
133
from jsonrpc import dispatcher, JSONRPCResponseManager
134
from jsonrpc.exceptions import JSONRPCDispatchException
135
136
@dispatcher.add_method
137
def divide(a, b):
138
if b == 0:
139
raise JSONRPCDispatchException(
140
code=-32602,
141
message="Division by zero",
142
data={"dividend": a, "divisor": b}
143
)
144
return a / b
145
146
@dispatcher.add_method
147
def validate_age(age):
148
if not isinstance(age, int) or age < 0:
149
raise JSONRPCDispatchException(
150
code=-32602,
151
message="Invalid age parameter",
152
data={"received": age, "expected": "positive integer"}
153
)
154
return {"valid": True, "age": age}
155
156
# Error response
157
request = '{"jsonrpc": "2.0", "method": "divide", "params": [10, 0], "id": 1}'
158
response = JSONRPCResponseManager.handle(request, dispatcher)
159
print(response.json)
160
# {
161
# "jsonrpc": "2.0",
162
# "error": {
163
# "code": -32602,
164
# "message": "Division by zero",
165
# "data": {"dividend": 10, "divisor": 0}
166
# },
167
# "id": 1
168
# }
169
```
170
171
### Predefined Error Types
172
173
```python
174
from jsonrpc.exceptions import (
175
JSONRPCMethodNotFound,
176
JSONRPCInvalidParams,
177
JSONRPCInternalError
178
)
179
180
# Method not found (automatically handled by manager)
181
request = '{"jsonrpc": "2.0", "method": "nonexistent", "id": 1}'
182
response = JSONRPCResponseManager.handle(request, dispatcher)
183
print(response.json)
184
# {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": 1}
185
186
# Invalid parameters (automatically detected)
187
@dispatcher.add_method
188
def greet(name, greeting="Hello"):
189
return f"{greeting}, {name}!"
190
191
# Missing required parameter
192
request = '{"jsonrpc": "2.0", "method": "greet", "params": [], "id": 1}'
193
response = JSONRPCResponseManager.handle(request, dispatcher)
194
print(response.json)
195
# {"jsonrpc": "2.0", "error": {"code": -32602, "message": "Invalid params", "data": {...}}, "id": 1}
196
197
# Too many parameters
198
request = '{"jsonrpc": "2.0", "method": "greet", "params": ["Alice", "Hi", "Extra"], "id": 1}'
199
response = JSONRPCResponseManager.handle(request, dispatcher)
200
print(response.json)
201
# {"jsonrpc": "2.0", "error": {"code": -32602, "message": "Invalid params", "data": {...}}, "id": 1}
202
```
203
204
### Custom Error Creation
205
206
```python
207
from jsonrpc.exceptions import JSONRPCError
208
209
# Create custom error
210
custom_error = JSONRPCError(
211
code=-32001,
212
message="Database connection failed",
213
data={
214
"database": "users",
215
"host": "localhost:5432",
216
"timestamp": "2023-01-01T12:00:00Z"
217
}
218
)
219
220
print(custom_error.json)
221
# {
222
# "code": -32001,
223
# "message": "Database connection failed",
224
# "data": {
225
# "database": "users",
226
# "host": "localhost:5432",
227
# "timestamp": "2023-01-01T12:00:00Z"
228
# }
229
# }
230
231
# Use in dispatch exception
232
@dispatcher.add_method
233
def get_user(user_id):
234
try:
235
# Simulate database operation
236
if user_id == 999:
237
raise ConnectionError("Database unavailable")
238
return {"id": user_id, "name": f"User {user_id}"}
239
except ConnectionError as e:
240
raise JSONRPCDispatchException(
241
code=-32001,
242
message="Database connection failed",
243
data={"error": str(e), "user_id": user_id}
244
)
245
```
246
247
### Error Response Structure
248
249
```python
250
from jsonrpc.exceptions import JSONRPCError
251
from jsonrpc.jsonrpc2 import JSONRPC20Response
252
253
# Manual error response creation
254
error = JSONRPCError(
255
code=-32603,
256
message="Internal error",
257
data={"component": "user_service", "error_id": "USR_001"}
258
)
259
260
response = JSONRPC20Response(error=error._data, _id=123)
261
print(response.json)
262
# {
263
# "jsonrpc": "2.0",
264
# "error": {
265
# "code": -32603,
266
# "message": "Internal error",
267
# "data": {"component": "user_service", "error_id": "USR_001"}
268
# },
269
# "id": 123
270
# }
271
```
272
273
### Parse and Request Errors
274
275
```python
276
from jsonrpc import JSONRPCResponseManager
277
278
# Parse error (invalid JSON)
279
invalid_json = '{"jsonrpc": "2.0", "method": "test", invalid}'
280
response = JSONRPCResponseManager.handle(invalid_json, dispatcher)
281
print(response.json)
282
# {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}
283
284
# Invalid request structure
285
invalid_request = '{"jsonrpc": "2.0", "params": [1, 2], "id": 1}' # Missing method
286
response = JSONRPCResponseManager.handle(invalid_request, dispatcher)
287
print(response.json)
288
# {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}
289
290
# Batch with invalid request
291
batch_with_error = '''[
292
{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": "1"},
293
{"jsonrpc": "2.0", "params": [3, 4], "id": "2"},
294
{"jsonrpc": "2.0", "method": "multiply", "params": [5, 6], "id": "3"}
295
]'''
296
response = JSONRPCResponseManager.handle(batch_with_error, dispatcher)
297
# Will return error for the invalid request in the batch
298
```
299
300
### Exception Handling in Methods
301
302
```python
303
from jsonrpc.exceptions import JSONRPCDispatchException
304
import logging
305
306
logger = logging.getLogger(__name__)
307
308
@dispatcher.add_method
309
def process_file(filename):
310
try:
311
# Simulate file processing
312
if not filename:
313
raise ValueError("Filename cannot be empty")
314
315
if not filename.endswith('.txt'):
316
raise JSONRPCDispatchException(
317
code=-32602,
318
message="Invalid file type",
319
data={"filename": filename, "supported": [".txt"]}
320
)
321
322
# Simulate processing
323
return {"status": "processed", "filename": filename}
324
325
except ValueError as e:
326
logger.error(f"Validation error: {e}")
327
raise JSONRPCDispatchException(
328
code=-32602,
329
message="Validation failed",
330
data={"error": str(e)}
331
)
332
except Exception as e:
333
logger.exception("Unexpected error processing file")
334
raise JSONRPCDispatchException(
335
code=-32603,
336
message="Internal processing error",
337
data={"filename": filename, "error_type": type(e).__name__}
338
)
339
340
# Test error cases
341
request1 = '{"jsonrpc": "2.0", "method": "process_file", "params": [""], "id": 1}'
342
response1 = JSONRPCResponseManager.handle(request1, dispatcher)
343
# Returns validation error
344
345
request2 = '{"jsonrpc": "2.0", "method": "process_file", "params": ["doc.pdf"], "id": 2}'
346
response2 = JSONRPCResponseManager.handle(request2, dispatcher)
347
# Returns invalid file type error
348
```
349
350
### Error Code Conventions
351
352
```python
353
# Standard JSON-RPC error codes
354
PARSE_ERROR = -32700 # Invalid JSON
355
INVALID_REQUEST = -32600 # Invalid request object
356
METHOD_NOT_FOUND = -32601 # Method doesn't exist
357
INVALID_PARAMS = -32602 # Invalid parameters
358
INTERNAL_ERROR = -32603 # Internal JSON-RPC error
359
360
# Server error range: -32000 to -32099 (reserved for implementation)
361
DATABASE_ERROR = -32001
362
AUTHENTICATION_ERROR = -32002
363
AUTHORIZATION_ERROR = -32003
364
RATE_LIMIT_ERROR = -32004
365
VALIDATION_ERROR = -32005
366
367
# Application-specific errors: -32100 and below
368
USER_NOT_FOUND = -32100
369
INSUFFICIENT_FUNDS = -32101
370
PRODUCT_OUT_OF_STOCK = -32102
371
372
@dispatcher.add_method
373
def transfer_funds(from_account, to_account, amount):
374
if amount <= 0:
375
raise JSONRPCDispatchException(
376
code=VALIDATION_ERROR,
377
message="Invalid transfer amount",
378
data={"amount": amount}
379
)
380
381
# Check balance
382
if get_balance(from_account) < amount:
383
raise JSONRPCDispatchException(
384
code=INSUFFICIENT_FUNDS,
385
message="Insufficient funds for transfer",
386
data={
387
"from_account": from_account,
388
"requested": amount,
389
"available": get_balance(from_account)
390
}
391
)
392
393
# Perform transfer
394
return {"status": "success", "transaction_id": "TXN123"}
395
```