A modern Python web server and web framework designed for high performance and speed using async/await syntax.
—
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.
Core exception classes that form the foundation of Sanic's error handling system.
class SanicException(Exception):
"""
Base exception class for all Sanic exceptions.
Attributes:
- message: Exception message
- status_code: HTTP status code (if applicable)
- quiet: Whether to suppress logging
"""
def __init__(
self,
message: str,
status_code: int = None,
quiet: bool = None,
context: dict = None,
extra: dict = None,
):
"""
Initialize Sanic exception.
Parameters:
- message: Error message
- status_code: HTTP status code
- quiet: Suppress logging if True
- context: Additional context information
- extra: Extra data for exception
"""
class ServerError(SanicException):
"""
Base class for 5xx server error exceptions.
Default status_code: 500
"""
class InvalidUsage(SanicException):
"""
Base class for 4xx client error exceptions.
Default status_code: 400
"""Exceptions for client-side errors with appropriate HTTP status codes.
class BadRequest(InvalidUsage):
"""
400 Bad Request exception.
Raised when the request is malformed or invalid.
"""
def __init__(self, message: str = "Bad Request", **kwargs):
super().__init__(message, status_code=400, **kwargs)
class Unauthorized(InvalidUsage):
"""
401 Unauthorized exception.
Raised when authentication is required but missing or invalid.
"""
def __init__(self, message: str = "Unauthorized", **kwargs):
super().__init__(message, status_code=401, **kwargs)
class Forbidden(InvalidUsage):
"""
403 Forbidden exception.
Raised when the request is understood but access is denied.
"""
def __init__(self, message: str = "Forbidden", **kwargs):
super().__init__(message, status_code=403, **kwargs)
class NotFound(InvalidUsage):
"""
404 Not Found exception.
Raised when the requested resource cannot be found.
"""
def __init__(self, message: str = "Not Found", **kwargs):
super().__init__(message, status_code=404, **kwargs)
class MethodNotAllowed(InvalidUsage):
"""
405 Method Not Allowed exception.
Raised when the HTTP method is not allowed for the resource.
"""
def __init__(self, message: str = "Method Not Allowed", **kwargs):
super().__init__(message, status_code=405, **kwargs)
class RangeNotSatisfiable(InvalidUsage):
"""
416 Range Not Satisfiable exception.
Raised when the requested range cannot be satisfied.
"""
def __init__(self, message: str = "Range Not Satisfiable", **kwargs):
super().__init__(message, status_code=416, **kwargs)
class ExpectationFailed(InvalidUsage):
"""
417 Expectation Failed exception.
Raised when the server cannot meet the Expect request header requirements.
"""
def __init__(self, message: str = "Expectation Failed", **kwargs):
super().__init__(message, status_code=417, **kwargs)
class PayloadTooLarge(InvalidUsage):
"""
413 Payload Too Large exception.
Raised when the request payload exceeds size limits.
"""
def __init__(self, message: str = "Payload Too Large", **kwargs):
super().__init__(message, status_code=413, **kwargs)
class RequestTimeout(InvalidUsage):
"""
408 Request Timeout exception.
Raised when the request takes too long to process.
"""
def __init__(self, message: str = "Request Timeout", **kwargs):
super().__init__(message, status_code=408, **kwargs)Exceptions for server-side errors indicating internal problems.
class InternalServerError(ServerError):
"""
500 Internal Server Error exception.
Raised when an unexpected server error occurs.
"""
def __init__(self, message: str = "Internal Server Error", **kwargs):
super().__init__(message, status_code=500, **kwargs)
class ServiceUnavailable(ServerError):
"""
503 Service Unavailable exception.
Raised when the server is temporarily unable to handle requests.
"""
def __init__(self, message: str = "Service Unavailable", **kwargs):
super().__init__(message, status_code=503, **kwargs)
class NotImplemented(ServerError):
"""
501 Not Implemented exception.
Raised when the requested functionality is not implemented.
"""
def __init__(self, message: str = "Not Implemented", **kwargs):
super().__init__(message, status_code=501, **kwargs)
class BadGateway(ServerError):
"""
502 Bad Gateway exception.
Raised when acting as gateway and receiving invalid response.
"""
def __init__(self, message: str = "Bad Gateway", **kwargs):
super().__init__(message, status_code=502, **kwargs)
class GatewayTimeout(ServerError):
"""
504 Gateway Timeout exception.
Raised when acting as gateway and upstream server times out.
"""
def __init__(self, message: str = "Gateway Timeout", **kwargs):
super().__init__(message, status_code=504, **kwargs)Framework-specific exceptions for particular scenarios and conditions.
class HeaderNotFound(SanicException):
"""
Raised when a required header is missing from the request.
"""
def __init__(self, message: str = "Header not found", **kwargs):
super().__init__(message, **kwargs)
class InvalidHeader(SanicException):
"""
Raised when a header value is invalid or malformed.
"""
def __init__(self, message: str = "Invalid header", **kwargs):
super().__init__(message, **kwargs)
class FileNotFound(NotFound):
"""
Raised when a requested file cannot be found.
"""
def __init__(self, message: str = "File not found", **kwargs):
super().__init__(message, **kwargs)
class WebsocketClosed(SanicException):
"""
Raised when attempting to use a closed WebSocket connection.
"""
def __init__(self, message: str = "WebSocket connection closed", **kwargs):
super().__init__(message, **kwargs)
class InvalidSignal(SanicException):
"""
Raised when an invalid signal is used.
"""
def __init__(self, message: str = "Invalid signal", **kwargs):
super().__init__(message, **kwargs)
class ContentRangeError(SanicException):
"""
Raised when there's an error with content range handling.
"""
def __init__(self, message: str = "Content range error", **kwargs):
super().__init__(message, **kwargs)
class HeaderExpectationFailed(SanicException):
"""
Raised when header expectations cannot be met.
"""
def __init__(self, message: str = "Header expectation failed", **kwargs):
super().__init__(message, **kwargs)
class MethodNotSupported(SanicException):
"""
Raised when an unsupported HTTP method is used.
"""
def __init__(self, message: str = "Method not supported", **kwargs):
super().__init__(message, **kwargs)Decorators and methods for registering custom exception handlers.
def exception(*exceptions):
"""
Decorator for registering exception handlers.
Parameters:
- *exceptions: Exception classes to handle
Usage:
@app.exception(NotFound, FileNotFound)
async def handle_not_found(request, exception):
return json({"error": "Resource not found"}, status=404)
"""
class ErrorHandler:
"""Error handler management for applications and blueprints."""
def add(self, exception, handler):
"""
Add exception handler.
Parameters:
- exception: Exception class
- handler: Handler function
"""
def lookup(self, exception, route_name: str = None):
"""
Look up handler for exception.
Parameters:
- exception: Exception instance
- route_name: Route name for context
Returns:
Handler function or None
"""
def response(self, request, exception):
"""
Generate response for exception.
Parameters:
- request: Request object
- exception: Exception instance
Returns:
HTTPResponse object
"""from sanic import Sanic
from sanic.response import json
from sanic.exceptions import NotFound, ServerError, BadRequest
app = Sanic("MyApp")
@app.route("/users/<user_id:int>")
async def get_user(request, user_id):
user = await fetch_user(user_id)
if not user:
raise NotFound("User not found")
return json({"user": user})
@app.route("/api/data", methods=["POST"])
async def process_data(request):
if not request.json:
raise BadRequest("JSON data required")
try:
result = await process_user_data(request.json)
return json({"result": result})
except ValueError as e:
raise BadRequest(f"Invalid data: {str(e)}")
except Exception as e:
raise ServerError("Processing failed")from sanic import Sanic
from sanic.response import json
from sanic.exceptions import NotFound, ServerError, SanicException
app = Sanic("MyApp")
@app.exception(NotFound)
async def handle_not_found(request, exception):
return json({
"error": "Resource not found",
"message": str(exception),
"path": request.path
}, status=404)
@app.exception(ServerError)
async def handle_server_error(request, exception):
# Log the error
app.logger.error(f"Server error: {exception}")
return json({
"error": "Internal server error",
"message": "An unexpected error occurred"
}, status=500)
@app.exception(SanicException)
async def handle_sanic_exception(request, exception):
"""Catch-all handler for Sanic exceptions."""
return json({
"error": "Application error",
"message": str(exception),
"status_code": getattr(exception, "status_code", 500)
}, status=getattr(exception, "status_code", 500))from sanic.exceptions import SanicException
from sanic.response import json
class ValidationError(SanicException):
\"\"\"Custom validation error exception.\"\"\"
def __init__(self, message: str, field: str = None, **kwargs):
super().__init__(message, status_code=422, **kwargs)
self.field = field
class AuthenticationError(SanicException):
\"\"\"Custom authentication error exception.\"\"\"
def __init__(self, message: str = "Authentication failed", **kwargs):
super().__init__(message, status_code=401, **kwargs)
class RateLimitExceeded(SanicException):
\"\"\"Custom rate limit exception.\"\"\"
def __init__(self, message: str = "Rate limit exceeded", retry_after: int = None, **kwargs):
super().__init__(message, status_code=429, **kwargs)
self.retry_after = retry_after
# Register handlers for custom exceptions
@app.exception(ValidationError)
async def handle_validation_error(request, exception):
return json({
"error": "Validation failed",
"message": str(exception),
"field": getattr(exception, "field", None)
}, status=422)
@app.exception(AuthenticationError)
async def handle_auth_error(request, exception):
return json({
"error": "Authentication required",
"message": str(exception)
}, status=401)
@app.exception(RateLimitExceeded)
async def handle_rate_limit(request, exception):
headers = {}
if hasattr(exception, "retry_after"):
headers["Retry-After"] = str(exception.retry_after)
return json({
"error": "Rate limit exceeded",
"message": str(exception)
}, status=429, headers=headers)from sanic import Sanic
from sanic.response import json
from sanic.exceptions import SanicException
app = Sanic("MyApp")
class DatabaseError(SanicException):
\"\"\"Database operation error with context.\"\"\"
def __init__(self, message: str, query: str = None, **kwargs):
context = {"query": query} if query else {}
super().__init__(message, status_code=500, context=context, **kwargs)
@app.exception(DatabaseError)
async def handle_database_error(request, exception):
# Log detailed error information
app.logger.error(f"Database error: {exception}")
if hasattr(exception, "context") and exception.context:
app.logger.error(f"Query: {exception.context.get('query')}")
# Return appropriate response
if app.config.DEBUG:
return json({
"error": "Database error",
"message": str(exception),
"context": getattr(exception, "context", {})
}, status=500)
else:
return json({
"error": "Internal server error",
"message": "A database error occurred"
}, status=500)
@app.route("/users")
async def get_users(request):
try:
query = "SELECT * FROM users"
users = await execute_query(query)
return json({"users": users})
except Exception as e:
raise DatabaseError("Failed to fetch users", query=query)from sanic import Blueprint
from sanic.response import json
from sanic.exceptions import NotFound
api_bp = Blueprint("api", url_prefix="/api")
class APIError(SanicException):
\"\"\"API-specific error exception.\"\"\"
def __init__(self, message: str, error_code: str = None, **kwargs):
super().__init__(message, status_code=400, **kwargs)
self.error_code = error_code
@api_bp.exception(APIError)
async def handle_api_error(request, exception):
return json({
"error": "API Error",
"message": str(exception),
"error_code": getattr(exception, "error_code", "UNKNOWN"),
"timestamp": datetime.utcnow().isoformat()
}, status=exception.status_code)
@api_bp.exception(NotFound)
async def handle_api_not_found(request, exception):
return json({
"error": "API Resource Not Found",
"message": "The requested API endpoint does not exist",
"path": request.path
}, status=404)
app.blueprint(api_bp)Install with Tessl CLI
npx tessl i tessl/pypi-sanic