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.

See More

You need to be signed in to leave a comment and join the discussion