209

What types of exceptions should be thrown for invalid or unexpected parameters in .NET? When would I choose one instead of another?

Follow-up:

Which exception would you use if you have a function expecting an integer corresponding to a month and you passed in '42'? Would this fall into the "out of range" category even though it's not a collection?

Even Mien
  • 44,393
  • 43
  • 115
  • 119
  • While I agree with most of given answers (as they seem reasonable enough), [microsoft clearly states](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception?redirectedfrom=MSDN&view=net-5.0#remarks) that in this specific case it should be `ArgumentOutOfRangeException`. In case the link changes, here's the relevant part: `ArgumentOutOfRangeException when the value of an argument is outside the range of acceptable values; for example, when the value "46" is passed as the month argument during the creation of a DateTime`. – Joel Sep 20 '21 at 15:22

7 Answers7

315

I like to use: ArgumentException, ArgumentNullException, and ArgumentOutOfRangeException.

There are other options, too, that do not focus so much on the argument itself, but rather judge the call as a whole:

  • InvalidOperationException – The argument might be OK, but not in the current state of the object. Credit goes to STW (previously Yoooder). Vote his answer up as well.
  • NotSupportedException – The arguments passed in are valid, but just not supported in this implementation. Imagine an FTP client, and you pass a command in that the client doesn’t support.

The trick is to throw the exception that best expresses why the method cannot be called the way it is. Ideally, the exception should be detailed about what went wrong, why it is wrong, and how to fix it.

I love when error messages point to help, documentation, or other resources. For example, Microsoft did a good first step with their KB articles, e.g. “Why do I receive an "Operation aborted" error message when I visit a Web page in Internet Explorer?”. When you encounter the error, they point you to the KB article in the error message. What they don’t do well is that they don’t tell you, why specifically it failed.

Thanks to STW (ex Yoooder) again for the comments.


In response to your followup, I would throw an ArgumentOutOfRangeException. Look at what MSDN says about this exception:

ArgumentOutOfRangeException is thrown when a method is invoked and at least one of the arguments passed to the method is not null reference (Nothing in Visual Basic) and does not contain a valid value.

So, in this case, you are passing a value, but that is not a valid value, since your range is 1–12. However, the way you document it makes it clear, what your API throws. Because although I might say ArgumentOutOfRangeException, another developer might say ArgumentException. Make it easy and document the behavior.

Community
  • 1
  • 1
JoshBerke
  • 66,142
  • 25
  • 126
  • 164
  • Psst! I agree that you answered his specific question exactly right, but see my answer below to help round out defensive coding and validating parameters ;-D – STW Apr 21 '09 at 19:57
  • 1
    +1 but documenting what exception is thrown and why is more important than picking the 'right' one. – pipTheGeek Apr 21 '09 at 21:10
  • 1
    @pipTheGeek - I think it's really a moot point. While documenting is definately important it also expects the consuming developer to be proactive or defensive and to actually read the documentation in detail. I would opt for a friendly/descriptive error over good documentation since the end-user has the chance of seeing one of them and not the other; there's a better chance of having an end-user communicate a descriptive error to a poor programmer than a poor programmer reading the full docs – STW Apr 21 '09 at 21:16
  • 5
    Note, if you catch ArgumentException, it will also catch ArgumentOutOfRange. – Steven Evers Apr 21 '09 at 22:53
  • How about `FormatException`: The exception that is thrown when the format of an argument is invalid, or when a composite format string is not well formed. – Anthony Sep 11 '18 at 00:58
50

I voted for Josh's answer, but would like to add one more to the list:

System.InvalidOperationException should be thrown if the argument is valid, but the object is in a state where the argument shouldn't be used.

Update Taken from MSDN:

InvalidOperationException is used in cases when the failure to invoke a method is caused by reasons other than invalid arguments.

Let's say that your object has a PerformAction(enmSomeAction action) method, valid enmSomeActions are Open and Close. If you call PerformAction(enmSomeAction.Open) twice in a row then the second call should throw the InvalidOperationException (since the arugment was valid, but not for the current state of the control)

Since you're already doing the right thing by programming defensively I have one other exception to mention is ObjectDisposedException. If your object implements IDisposable then you should always have a class variable tracking the disposed state; if your object has been disposed and a method gets called on it you should raise the ObjectDisposedException:

public void SomeMethod()
{
    If (m_Disposed) {
          throw new ObjectDisposedException("Object has been disposed")
     }
    // ... Normal execution code
}

Update: To answer your follow-up: It is a bit of an ambiguous situation, and is made a little more complicated by a generic (not in the .NET Generics sense) data type being used to represent a specific set of data; an enum or other strongly typed object would be a more ideal fit--but we don't always have that control.

I would personally lean towards the ArgumentOutOfRangeException and provide a message that indicates the valid values are 1-12. My reasoning is that when you talk about months, assuming all integer representations of months are valid, then you are expecting a value in the range of 1-12. If only certain months (like months that had 31 days) were valid then you would not be dealing with a Range per-se and I would throw a generic ArgumentException that indicated the valid values, and I would also document them in the method's comments.

Community
  • 1
  • 1
STW
  • 44,917
  • 17
  • 105
  • 161
40

Depending on the actual value and what exception fits best:

If this is not precise enough, just derive your own exception class from ArgumentException.

Yoooder's answer enlightened me. An input is invalid if it is not valid at any time, while an input is unexpected if it is not valid for the current state of the system. So in the later case an InvalidOperationException is a reasonable choice.

Slipp D. Thompson
  • 33,165
  • 3
  • 43
  • 43
Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
  • 9
    Taken from MSDN's page on InvalidOperationException: "InvalidOperationException is used in cases when the failure to invoke a method is caused by reasons other than invalid arguments." – STW Apr 21 '09 at 19:44
6

argument exception.

  • System.ArgumentException
  • System.ArgumentNullException
  • System.ArgumentOutOfRangeException
Syed Tayyab Ali
  • 3,643
  • 7
  • 31
  • 36
4

ArgumentException:

ArgumentException is thrown when a method is invoked and at least one of the passed arguments does not meet the parameter specification of the called method. All instances of ArgumentException should carry a meaningful error message describing the invalid argument, as well as the expected range of values for the argument.

A few subclasses also exist for specific types of invalidity. The link has summaries of the subtypes and when they should apply.

Ben S
  • 68,394
  • 30
  • 171
  • 212
0

There is a standard ArgumentException that you could use, or you could subclass and make your own. There are several specific ArgumentException classes:

http://msdn.microsoft.com/en-us/library/system.argumentexception(VS.71).aspx

Whichever one works best.

Scott M.
  • 7,313
  • 30
  • 39
  • 1
    I disagree for nearly all cases; the provided .NET Argument*Exception classes are very commonly used and provide you the ability to provide enough specific information to notify the consumer of the issue. – STW Apr 21 '09 at 19:47
  • To clarify - I disagree for nearly all cases of deriving from the Argument*Exception classes. Using one of the .NET Argument Exceptions plus a descriptive and clear message provides enough detail for more or less every situation where arguments are invalid. – STW Apr 21 '09 at 20:43
  • agreed, but i was just describing the options available. I definitely should have been more clear about the "preferred" method. – Scott M. Apr 21 '09 at 20:45
-1

Short answer:
Neither

Longer answer:
using Argument*Exception (except in a library that is a product on its on, such as component library) is a smell. Exceptions are to handle exceptional situation, not bugs, and not user's (i.e. API consumer) shortfalls.

Longest answer:
Throwing exceptions for invalid arguments is rude, unless you write a library.
I prefer using assertions, for two (or more) reasons:

  • Assertions don't need to be tested, while throw assertions do, and test against ArgumentNullException looks ridiculous (try it).
  • Assertions better communicate the intended use of the unit, and is closer to being executable documentation than a class behavior specification.
  • You can change behavior of assertion violation. For example in debug compilation a message box is fine, so that your QA will hit you with it right away (you also get your IDE breaking on the line where it happens), while in unit test you can indicate assertion failure as a test failure.

Here is what handling of null exception looks like (being sarcastic, obviously):

try {
    library.Method(null);
}
catch (ArgumentNullException e) {
    // retry with real argument this time
    library.Method(realArgument);
}

Exceptions shall be used when situation is expected but exceptional (things happen that are outside of consumer's control, such as IO failure). Argument*Exception is an indication of a bug and shall be (my opinion) handled with tests and assisted with Debug.Assert

BTW: In this particular case, you could have used Month type, instead of int. C# falls short when it comes to type safety (Aspect# rulez!) but sometimes you can prevent (or catch at compile time) those bugs all together.

And yes, MicroSoft is wrong about that.

THX-1138
  • 21,316
  • 26
  • 96
  • 160
  • 9
    IMHO, exceptions should also be thrown when the called method cannot reasonably proceed. That includes the case when the caller has passed bogus arguments. What would you do instead? Return -1? – John Saunders Apr 21 '09 at 21:58
  • 1
    If invalid arguments would cause an inner function to fail, what are the pros and cons of testing arguments for validity, versus catching an InvalidArgumentException from the inner function and wrapping it with a more informative one? The latter approach would seem to improve performance in the common case, but I've not seen it done much. – supercat Mar 18 '11 at 17:08
  • A quick google search around this question indicates that throwing the general Exception is the worst practice of all. In regards to an assertion of the argument, I see merit in this on small personal projects, but not on enterprise applications where invalid arguments are most likely due to a bad configuration or poor understanding of the application. – Max Jun 06 '19 at 17:15
  • How on Earth would you invent the real value of the argument? Just propagate the ArgumentNullException (or some exception wrapping it) to the topmost point and handle it there (display some message to the user, log it, return an appropriate exit code to the console, whatever). Exceptions are to be used when the function cannot fulfill its contract, and usually there is no way to handle them except to abort the operation and indicate the failure to the user. – Mike Rosoft Sep 16 '21 at 07:59
  • An ArgumentNullException should never be caught - because it basically says there's a bug in the invoking code. But finding that bug becomes much easier when you have a clear ArgumentNullException instead of a NullReferenceException somewhere far removed from the original call. – TravelingFox Nov 23 '21 at 10:30