Python's built-in exceptions cover many cases, but complex applications often require domain-specific error handling. Creating custom exceptions allows you to communicate what went wrong in the language of your specific domain, rather than the language of the Python interpreter.
Inheriting from Exception
All custom user-defined exceptions should inherit from the built-in Exception class (or a subclass of it), not BaseException. This ensures they are caught by standard try...except Exception blocks but allows system-exiting exceptions (like KeyboardInterrupt) to pass through.