Which exception should I raise on bad/illegal argument combinations in Python?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
For bad combinations of otherwise valid arguments in Python, ValueError is usually the right built-in exception. The general rule is simple: raise TypeError when the argument type is wrong, and raise ValueError when the type is acceptable but the values or value combination are not.
Why ValueError Is Usually Correct
Suppose a function accepts two optional arguments but they cannot be used together. Each argument may be the right type on its own, yet the combination is invalid. That is a value-level problem, not a type-level problem.
This is the standard Python style. The caller supplied arguments of acceptable kinds, but in a combination the function does not permit.
When TypeError Is Better
If the function is called with an argument of the wrong type, use TypeError.
This split keeps the error semantics clear:
- wrong kind of object:
TypeError - acceptable kind, unacceptable value:
ValueError
Illegal Combinations Are Still Value Problems
A few examples that usually justify ValueError:
- '
startgreater thanend' - both
pathandfileobjprovided when only one is allowed - '
min_valuelarger thanmax_value' - two flags enabled together when they are mutually exclusive
In all of these, the problem is not that Python cannot interpret the arguments. The problem is that the function contract rejects the value combination.
When a Custom Exception Makes Sense
A custom exception is worth introducing only when the failure is domain-specific and callers need to catch it separately from generic argument validation.
Notice that the custom exception still inherits from ValueError. That preserves the general meaning while adding domain semantics.
Make the Message Actionable
The exception class matters, but so does the message. A good validation error tells the caller what rule was broken.
Bad:
Better:
Callers should not have to reverse-engineer your API contract from a vague error string.
Common Pitfalls
- Raising
TypeErrorfor mutually exclusive but correctly typed arguments blurs Python's usual error semantics. UseValueErrorwhen the values or their combination are the issue. - Inventing a custom exception too early adds API surface without much benefit. Start with built-in exceptions unless callers truly need domain-specific handling.
- Returning silently adjusted values instead of raising an error can hide misuse of the function contract. Reject invalid combinations explicitly when the rule matters.
- Using vague messages such as
bad argumentsmakes debugging harder than it needs to be. State the violated rule directly. - Validating only each argument individually while ignoring cross-argument rules leaves the real contract unenforced. Check combinations, not just single parameters.
Summary
- For illegal combinations of otherwise valid arguments,
ValueErroris usually the right exception. - Use
TypeErrorwhen the argument type itself is wrong. - Cross-argument validation is still value validation, not type validation.
- Custom exceptions are useful only when callers need domain-specific handling.
- The exception message should describe the violated rule clearly.

