Error Handling Patterns
Errors are inevitable. Networks fail, databases go down, users provide unexpected input. The difference between a frustrating application and a professional one is how it handles these errors.
The Two Audiences for Errors
When an error occurs, you need to communicate with two different audiences:
Users need to know something went wrong and what they can do about it. They don't need technical details — those are confusing and potentially reveal security information.
Developers (including future you) need detailed information to diagnose and fix the problem. Stack traces, request details, and timestamps are essential.
Never show users the information meant for developers.
Try/Except Patterns
Wrap code that might fail in try/except blocks:
@app.route('/todos', methods=['POST'])
def create_todo():
try:
text = request.json.get('text', '')
todo = save_todo(text)
return jsonify(todo), 201
except ValueError as e:
# Known validation error - tell the user
return jsonify({'error': str(e)}), 400
except Exception as e:
# Unknown error - log it, give user generic message
logger.error(f"Failed to create todo: {e}")
return jsonify({'error': 'Something went wrong'}), 500
Catch specific exceptions when you know how to handle them. Catch generic exceptions as a safety net.
HTTP Status Codes
Use appropriate status codes so clients know what happened:
- 400 Bad Request: User sent invalid input
- 401 Unauthorized: User needs to log in
- 403 Forbidden: User doesn't have permission
- 404 Not Found: Resource doesn't exist
- 500 Internal Server Error: Something broke on the server
Clients can handle these differently — retrying on 500, prompting login on 401, showing validation errors on 400.
User-Friendly Error Messages
Good error messages are:
- Clear: "Please enter a todo" not "Validation failed"
- Actionable: Tell users what to do next
- Polite: Don't blame the user
Bad: "Error: NoneType has no attribute 'strip'" Good: "Please enter some text for your todo"
Logging Errors Properly
Log enough information to debug later:
logger.error(
f"Failed to create todo: {e}",
extra={
'user_id': current_user.id,
'request_data': request.json,
'timestamp': datetime.utcnow()
}
)
Include context that helps you reproduce the problem. But never log passwords, API keys, or sensitive personal data.
Failing Gracefully
When errors occur, preserve as much functionality as possible. If loading one component fails, don't crash the entire page. Show what you can and indicate what failed.
This approach — called graceful degradation — keeps your application usable even when things go wrong.