0
# Exception Handling
1
2
Sanic provides a comprehensive exception system with HTTP status code exceptions, custom error handlers, and mechanisms for graceful error handling. The framework includes built-in exceptions for common HTTP errors and supports custom exception handling.
3
4
## Capabilities
5
6
### Base Exception Classes
7
8
Core exception classes that form the foundation of Sanic's error handling system.
9
10
```python { .api }
11
class SanicException(Exception):
12
"""
13
Base exception class for all Sanic exceptions.
14
15
Attributes:
16
- message: Exception message
17
- status_code: HTTP status code (if applicable)
18
- quiet: Whether to suppress logging
19
"""
20
21
def __init__(
22
self,
23
message: str,
24
status_code: int = None,
25
quiet: bool = None,
26
context: dict = None,
27
extra: dict = None,
28
):
29
"""
30
Initialize Sanic exception.
31
32
Parameters:
33
- message: Error message
34
- status_code: HTTP status code
35
- quiet: Suppress logging if True
36
- context: Additional context information
37
- extra: Extra data for exception
38
"""
39
40
class ServerError(SanicException):
41
"""
42
Base class for 5xx server error exceptions.
43
44
Default status_code: 500
45
"""
46
47
class InvalidUsage(SanicException):
48
"""
49
Base class for 4xx client error exceptions.
50
51
Default status_code: 400
52
"""
53
```
54
55
### HTTP Client Error Exceptions (4xx)
56
57
Exceptions for client-side errors with appropriate HTTP status codes.
58
59
```python { .api }
60
class BadRequest(InvalidUsage):
61
"""
62
400 Bad Request exception.
63
64
Raised when the request is malformed or invalid.
65
"""
66
67
def __init__(self, message: str = "Bad Request", **kwargs):
68
super().__init__(message, status_code=400, **kwargs)
69
70
class Unauthorized(InvalidUsage):
71
"""
72
401 Unauthorized exception.
73
74
Raised when authentication is required but missing or invalid.
75
"""
76
77
def __init__(self, message: str = "Unauthorized", **kwargs):
78
super().__init__(message, status_code=401, **kwargs)
79
80
class Forbidden(InvalidUsage):
81
"""
82
403 Forbidden exception.
83
84
Raised when the request is understood but access is denied.
85
"""
86
87
def __init__(self, message: str = "Forbidden", **kwargs):
88
super().__init__(message, status_code=403, **kwargs)
89
90
class NotFound(InvalidUsage):
91
"""
92
404 Not Found exception.
93
94
Raised when the requested resource cannot be found.
95
"""
96
97
def __init__(self, message: str = "Not Found", **kwargs):
98
super().__init__(message, status_code=404, **kwargs)
99
100
class MethodNotAllowed(InvalidUsage):
101
"""
102
405 Method Not Allowed exception.
103
104
Raised when the HTTP method is not allowed for the resource.
105
"""
106
107
def __init__(self, message: str = "Method Not Allowed", **kwargs):
108
super().__init__(message, status_code=405, **kwargs)
109
110
class RangeNotSatisfiable(InvalidUsage):
111
"""
112
416 Range Not Satisfiable exception.
113
114
Raised when the requested range cannot be satisfied.
115
"""
116
117
def __init__(self, message: str = "Range Not Satisfiable", **kwargs):
118
super().__init__(message, status_code=416, **kwargs)
119
120
class ExpectationFailed(InvalidUsage):
121
"""
122
417 Expectation Failed exception.
123
124
Raised when the server cannot meet the Expect request header requirements.
125
"""
126
127
def __init__(self, message: str = "Expectation Failed", **kwargs):
128
super().__init__(message, status_code=417, **kwargs)
129
130
class PayloadTooLarge(InvalidUsage):
131
"""
132
413 Payload Too Large exception.
133
134
Raised when the request payload exceeds size limits.
135
"""
136
137
def __init__(self, message: str = "Payload Too Large", **kwargs):
138
super().__init__(message, status_code=413, **kwargs)
139
140
class RequestTimeout(InvalidUsage):
141
"""
142
408 Request Timeout exception.
143
144
Raised when the request takes too long to process.
145
"""
146
147
def __init__(self, message: str = "Request Timeout", **kwargs):
148
super().__init__(message, status_code=408, **kwargs)
149
```
150
151
### HTTP Server Error Exceptions (5xx)
152
153
Exceptions for server-side errors indicating internal problems.
154
155
```python { .api }
156
class InternalServerError(ServerError):
157
"""
158
500 Internal Server Error exception.
159
160
Raised when an unexpected server error occurs.
161
"""
162
163
def __init__(self, message: str = "Internal Server Error", **kwargs):
164
super().__init__(message, status_code=500, **kwargs)
165
166
class ServiceUnavailable(ServerError):
167
"""
168
503 Service Unavailable exception.
169
170
Raised when the server is temporarily unable to handle requests.
171
"""
172
173
def __init__(self, message: str = "Service Unavailable", **kwargs):
174
super().__init__(message, status_code=503, **kwargs)
175
176
class NotImplemented(ServerError):
177
"""
178
501 Not Implemented exception.
179
180
Raised when the requested functionality is not implemented.
181
"""
182
183
def __init__(self, message: str = "Not Implemented", **kwargs):
184
super().__init__(message, status_code=501, **kwargs)
185
186
class BadGateway(ServerError):
187
"""
188
502 Bad Gateway exception.
189
190
Raised when acting as gateway and receiving invalid response.
191
"""
192
193
def __init__(self, message: str = "Bad Gateway", **kwargs):
194
super().__init__(message, status_code=502, **kwargs)
195
196
class GatewayTimeout(ServerError):
197
"""
198
504 Gateway Timeout exception.
199
200
Raised when acting as gateway and upstream server times out.
201
"""
202
203
def __init__(self, message: str = "Gateway Timeout", **kwargs):
204
super().__init__(message, status_code=504, **kwargs)
205
```
206
207
### Specialized Exceptions
208
209
Framework-specific exceptions for particular scenarios and conditions.
210
211
```python { .api }
212
class HeaderNotFound(SanicException):
213
"""
214
Raised when a required header is missing from the request.
215
"""
216
217
def __init__(self, message: str = "Header not found", **kwargs):
218
super().__init__(message, **kwargs)
219
220
class InvalidHeader(SanicException):
221
"""
222
Raised when a header value is invalid or malformed.
223
"""
224
225
def __init__(self, message: str = "Invalid header", **kwargs):
226
super().__init__(message, **kwargs)
227
228
class FileNotFound(NotFound):
229
"""
230
Raised when a requested file cannot be found.
231
"""
232
233
def __init__(self, message: str = "File not found", **kwargs):
234
super().__init__(message, **kwargs)
235
236
class WebsocketClosed(SanicException):
237
"""
238
Raised when attempting to use a closed WebSocket connection.
239
"""
240
241
def __init__(self, message: str = "WebSocket connection closed", **kwargs):
242
super().__init__(message, **kwargs)
243
244
class InvalidSignal(SanicException):
245
"""
246
Raised when an invalid signal is used.
247
"""
248
249
def __init__(self, message: str = "Invalid signal", **kwargs):
250
super().__init__(message, **kwargs)
251
252
class ContentRangeError(SanicException):
253
"""
254
Raised when there's an error with content range handling.
255
"""
256
257
def __init__(self, message: str = "Content range error", **kwargs):
258
super().__init__(message, **kwargs)
259
260
class HeaderExpectationFailed(SanicException):
261
"""
262
Raised when header expectations cannot be met.
263
"""
264
265
def __init__(self, message: str = "Header expectation failed", **kwargs):
266
super().__init__(message, **kwargs)
267
268
class MethodNotSupported(SanicException):
269
"""
270
Raised when an unsupported HTTP method is used.
271
"""
272
273
def __init__(self, message: str = "Method not supported", **kwargs):
274
super().__init__(message, **kwargs)
275
```
276
277
### Exception Handlers
278
279
Decorators and methods for registering custom exception handlers.
280
281
```python { .api }
282
def exception(*exceptions):
283
"""
284
Decorator for registering exception handlers.
285
286
Parameters:
287
- *exceptions: Exception classes to handle
288
289
Usage:
290
@app.exception(NotFound, FileNotFound)
291
async def handle_not_found(request, exception):
292
return json({"error": "Resource not found"}, status=404)
293
"""
294
295
class ErrorHandler:
296
"""Error handler management for applications and blueprints."""
297
298
def add(self, exception, handler):
299
"""
300
Add exception handler.
301
302
Parameters:
303
- exception: Exception class
304
- handler: Handler function
305
"""
306
307
def lookup(self, exception, route_name: str = None):
308
"""
309
Look up handler for exception.
310
311
Parameters:
312
- exception: Exception instance
313
- route_name: Route name for context
314
315
Returns:
316
Handler function or None
317
"""
318
319
def response(self, request, exception):
320
"""
321
Generate response for exception.
322
323
Parameters:
324
- request: Request object
325
- exception: Exception instance
326
327
Returns:
328
HTTPResponse object
329
"""
330
```
331
332
## Usage Examples
333
334
### Basic Exception Handling
335
336
```python
337
from sanic import Sanic
338
from sanic.response import json
339
from sanic.exceptions import NotFound, ServerError, BadRequest
340
341
app = Sanic("MyApp")
342
343
@app.route("/users/<user_id:int>")
344
async def get_user(request, user_id):
345
user = await fetch_user(user_id)
346
if not user:
347
raise NotFound("User not found")
348
return json({"user": user})
349
350
@app.route("/api/data", methods=["POST"])
351
async def process_data(request):
352
if not request.json:
353
raise BadRequest("JSON data required")
354
355
try:
356
result = await process_user_data(request.json)
357
return json({"result": result})
358
except ValueError as e:
359
raise BadRequest(f"Invalid data: {str(e)}")
360
except Exception as e:
361
raise ServerError("Processing failed")
362
```
363
364
### Custom Exception Handlers
365
366
```python
367
from sanic import Sanic
368
from sanic.response import json
369
from sanic.exceptions import NotFound, ServerError, SanicException
370
371
app = Sanic("MyApp")
372
373
@app.exception(NotFound)
374
async def handle_not_found(request, exception):
375
return json({
376
"error": "Resource not found",
377
"message": str(exception),
378
"path": request.path
379
}, status=404)
380
381
@app.exception(ServerError)
382
async def handle_server_error(request, exception):
383
# Log the error
384
app.logger.error(f"Server error: {exception}")
385
386
return json({
387
"error": "Internal server error",
388
"message": "An unexpected error occurred"
389
}, status=500)
390
391
@app.exception(SanicException)
392
async def handle_sanic_exception(request, exception):
393
"""Catch-all handler for Sanic exceptions."""
394
return json({
395
"error": "Application error",
396
"message": str(exception),
397
"status_code": getattr(exception, "status_code", 500)
398
}, status=getattr(exception, "status_code", 500))
399
```
400
401
### Custom Exception Classes
402
403
```python
404
from sanic.exceptions import SanicException
405
from sanic.response import json
406
407
class ValidationError(SanicException):
408
\"\"\"Custom validation error exception.\"\"\"
409
410
def __init__(self, message: str, field: str = None, **kwargs):
411
super().__init__(message, status_code=422, **kwargs)
412
self.field = field
413
414
class AuthenticationError(SanicException):
415
\"\"\"Custom authentication error exception.\"\"\"
416
417
def __init__(self, message: str = "Authentication failed", **kwargs):
418
super().__init__(message, status_code=401, **kwargs)
419
420
class RateLimitExceeded(SanicException):
421
\"\"\"Custom rate limit exception.\"\"\"
422
423
def __init__(self, message: str = "Rate limit exceeded", retry_after: int = None, **kwargs):
424
super().__init__(message, status_code=429, **kwargs)
425
self.retry_after = retry_after
426
427
# Register handlers for custom exceptions
428
@app.exception(ValidationError)
429
async def handle_validation_error(request, exception):
430
return json({
431
"error": "Validation failed",
432
"message": str(exception),
433
"field": getattr(exception, "field", None)
434
}, status=422)
435
436
@app.exception(AuthenticationError)
437
async def handle_auth_error(request, exception):
438
return json({
439
"error": "Authentication required",
440
"message": str(exception)
441
}, status=401)
442
443
@app.exception(RateLimitExceeded)
444
async def handle_rate_limit(request, exception):
445
headers = {}
446
if hasattr(exception, "retry_after"):
447
headers["Retry-After"] = str(exception.retry_after)
448
449
return json({
450
"error": "Rate limit exceeded",
451
"message": str(exception)
452
}, status=429, headers=headers)
453
```
454
455
### Exception Context and Debugging
456
457
```python
458
from sanic import Sanic
459
from sanic.response import json
460
from sanic.exceptions import SanicException
461
462
app = Sanic("MyApp")
463
464
class DatabaseError(SanicException):
465
\"\"\"Database operation error with context.\"\"\"
466
467
def __init__(self, message: str, query: str = None, **kwargs):
468
context = {"query": query} if query else {}
469
super().__init__(message, status_code=500, context=context, **kwargs)
470
471
@app.exception(DatabaseError)
472
async def handle_database_error(request, exception):
473
# Log detailed error information
474
app.logger.error(f"Database error: {exception}")
475
if hasattr(exception, "context") and exception.context:
476
app.logger.error(f"Query: {exception.context.get('query')}")
477
478
# Return appropriate response
479
if app.config.DEBUG:
480
return json({
481
"error": "Database error",
482
"message": str(exception),
483
"context": getattr(exception, "context", {})
484
}, status=500)
485
else:
486
return json({
487
"error": "Internal server error",
488
"message": "A database error occurred"
489
}, status=500)
490
491
@app.route("/users")
492
async def get_users(request):
493
try:
494
query = "SELECT * FROM users"
495
users = await execute_query(query)
496
return json({"users": users})
497
except Exception as e:
498
raise DatabaseError("Failed to fetch users", query=query)
499
```
500
501
### Blueprint Exception Handlers
502
503
```python
504
from sanic import Blueprint
505
from sanic.response import json
506
from sanic.exceptions import NotFound
507
508
api_bp = Blueprint("api", url_prefix="/api")
509
510
class APIError(SanicException):
511
\"\"\"API-specific error exception.\"\"\"
512
513
def __init__(self, message: str, error_code: str = None, **kwargs):
514
super().__init__(message, status_code=400, **kwargs)
515
self.error_code = error_code
516
517
@api_bp.exception(APIError)
518
async def handle_api_error(request, exception):
519
return json({
520
"error": "API Error",
521
"message": str(exception),
522
"error_code": getattr(exception, "error_code", "UNKNOWN"),
523
"timestamp": datetime.utcnow().isoformat()
524
}, status=exception.status_code)
525
526
@api_bp.exception(NotFound)
527
async def handle_api_not_found(request, exception):
528
return json({
529
"error": "API Resource Not Found",
530
"message": "The requested API endpoint does not exist",
531
"path": request.path
532
}, status=404)
533
534
app.blueprint(api_bp)
535
```