An IllegalArgumentException is the unchecked exception that a Java method throws when a caller passes an argument with a value the method does not accept. Java’s own classes throw it for values such as an unknown enum name or a negative list size, and we throw it from our own methods when a caller sends a bad value such as a negative quantity.
The exception message shows the bad value. The first stack trace line in our own code shows where that value was passed in, so we fix the value there or check the input before the call.
The following example shows two common causes and the safe version of each call. Size is a small enum with the constants SMALL, MEDIUM and LARGE, and parse() is a lookup method that we add to it.
Size size = Size.valueOf("XL"); // IllegalArgumentException: No enum constant com.howtodoinjava.iae.Size.XL
Size safeSize = Size.parse("XL").orElse(Size.MEDIUM); // MEDIUM
int requested = -1;
List<String> fruits = new ArrayList<>(requested); // IllegalArgumentException: Illegal Capacity: -1
List<String> safeFruits = new ArrayList<>(Math.max(0, requested)); // []
Both messages contain the bad value (“XL” and -1), so the message alone tells us which argument to check. Next come the JDK calls that throw it most often, and when our own code should throw it instead of IllegalStateException or NullPointerException.
1. Reading a java.lang.IllegalArgumentException Stack Trace
The class java.lang.IllegalArgumentException extends RuntimeException, so it is an unchecked exception. The compiler does not force us to catch or declare it, so we first see it at runtime, in the console or a server log.
The following trace comes from a SizeDemo class whose main() method calls Size.valueOf(“XL”).
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant com.howtodoinjava.iae.Size.XL
at java.base/java.lang.Enum.valueOf(Enum.java:293)
at com.howtodoinjava.iae.Size.valueOf(Size.java:7)
at com.howtodoinjava.iae.SizeDemo.main(SizeDemo.java:15)
We read the trace in two steps.
- The first line gives the exception class and the message. The Javadoc of IllegalArgumentException says it is thrown when “a method has been passed an illegal or inappropriate argument”, so the message describes the argument, here the name “XL”.
- Each line that starts with “at” is one method call. The first one in our own package (SizeDemo.java:15) is the line that passed the bad argument. The JDK lines above it only rejected the value.
2. JDK Calls That Throw IllegalArgumentException, With Messages and Fixes
Most of these errors come from an unknown name, a number that is too small or text in the wrong format. These are the calls we see most often in app code, with the message each one prints on Java 25.
| Call | Message | Fix |
|---|---|---|
| Size.valueOf(“XL”) | No enum constant com.howtodoinjava.iae.Size.XL | Look up the name and return Optional |
| new ArrayList<>(-1) | Illegal Capacity: -1 | Raise a negative capacity to 0 with Math.max(0, n) |
| “ab”.repeat(-1) | count is negative: -1 | Check the computed count before the call |
| new Random().nextInt(0) | bound must be positive | Handle the empty case before picking an element |
| fruits.subList(2, 1) | fromIndex(2) > toIndex(1) | Put the smaller index first with Math.min() and Math.max() |
| UUID.fromString(“apple”) | Invalid UUID string: apple | Catch the exception at the input boundary |
| Base64.getDecoder().decode(“abc$”) | Illegal base64 character 24 | Catch the exception at the input boundary |
The Base64 message prints the bad character as a hex code, so 24 is the dollar sign. A few calls, such as thread.setPriority(11), throw it with no message at all, so the stack trace is our only hint.
2.1. Unknown Name in Enum.valueOf()
The valueOf() method of an enum accepts only the exact constant name. It does not ignore case or remove spaces, so “small” and “SMALL ” both fail, even though SMALL exists. We see this error when the name comes from a request parameter or from a CSV file that someone edited by hand.
Size lower = Size.valueOf("small"); // IllegalArgumentException: No enum constant com.howtodoinjava.iae.Size.small
Size padded = Size.valueOf("SMALL "); // IllegalArgumentException: No enum constant com.howtodoinjava.iae.Size.SMALL
The fix is a lookup method that removes the spaces, changes the name to upper case and returns an Optional instead of throwing. With it, Size.parse(” small “) returns Optional[SMALL], and the caller picks a default for unknown names with orElse().
public static Optional<Size> parse(String name) {
if (name == null || name.isBlank()) {
return Optional.empty();
}
String key = name.strip().toUpperCase(Locale.ROOT);
return Arrays.stream(values())
.filter(size -> size.name().equals(key))
.findFirst();
}
2.2. Negative Capacity, Count or Bound
A negative number reaches these methods when our code computes it, for example list.size() – 1 on an empty list or a page size read from configuration. Say a fruit shop app picks a “fruit of the day” with random.nextInt(fruits.size()). On the first day that the stock table is empty, nextInt(0) throws IllegalArgumentException: bound must be positive.
We fix the code that computes the number, not the JDK call. The capacity of an ArrayList is only its starting size, so raising a negative value to zero is safe. A count or a bound changes the result, so we handle the empty case first.
int requested = -1;
List<String> fruits = new ArrayList<>(Math.max(0, requested)); // []
List<String> stock = new ArrayList<>();
Random random = new Random();
Optional<String> pick = stock.isEmpty()
? Optional.empty()
: Optional.of(stock.get(random.nextInt(stock.size()))); // Optional.empty
2.3. Malformed Text in parseInt(), UUID.fromString() and Base64
Parsing methods throw IllegalArgumentException or its subclass NumberFormatException when the text has the wrong format. We cannot fully check typed-in text before parsing it. So we catch the exception in one place, where the input enters the app, and return an empty result. The String to int article covers number parsing.
public static Optional<UUID> parseUuid(String text) {
if (text == null) {
return Optional.empty();
}
try {
return Optional.of(UUID.fromString(text.strip()));
} catch (IllegalArgumentException e) {
return Optional.empty();
}
}
The parseQuantity() and decodeBase64() methods follow the same pattern, with Integer.parseInt() and Base64.getDecoder().decode() inside the try block.
OptionalInt bad = SafeInputs.parseQuantity("abc"); // OptionalInt.empty
Optional<UUID> id = SafeInputs.parseUuid("apple"); // Optional.empty
Optional<byte[]> bytes = SafeInputs.decodeBase64("abc$"); // Optional.empty
3. Subclasses of IllegalArgumentException in the JDK
Many parsing and formatting exceptions extend IllegalArgumentException, so a catch (IllegalArgumentException e) block also catches them. The Java 25 Javadoc lists 18 direct subclasses, and these four are the ones we meet most often.
| Subclass | Thrown by | Real message |
|---|---|---|
| NumberFormatException | Integer.parseInt(“abc”) | For input string: “abc” |
| PatternSyntaxException | Pattern.compile(“(apple”) | Unclosed group near index 6 |
| IllegalFormatConversionException | String.format(“%d”, “apple”) | d != java.lang.String |
| UnsupportedCharsetException | Charset.forName(“utf-99”) | utf-99 |
Some argument errors are not in this hierarchy. The java.time API throws its own DateTimeException, which extends RuntimeException and not IllegalArgumentException. For example, LocalDate.of(2026, 2, 30) throws DateTimeException: Invalid date ‘FEBRUARY 30’, and Duration.parse(“10s”) throws its subclass DateTimeParseException.
A catch (IllegalArgumentException e) block around a date call never runs, so for java.time calls we catch DateTimeException instead.
4. IllegalArgumentException vs IllegalStateException vs NullPointerException
When our own method rejects a call, the exception type tells the caller what went wrong. The IllegalStateException Javadoc says it signals “that a method has been invoked at an illegal or inappropriate time”. So we check whether the argument is wrong or the object is not ready for the call.
A fruit basket shows the difference. Adding -5 apples is wrong for every basket, so add() throws IllegalArgumentException. Adding 5 apples is a valid request, but it fails after close(), so add() throws IllegalStateException.
| Problem | Exception | JDK helper |
|---|---|---|
| A required argument is null | NullPointerException | Objects.requireNonNull() |
| An index is outside the valid range | IndexOutOfBoundsException | Objects.checkIndex() |
| An argument has a wrong value | IllegalArgumentException | none, we write an if and throw |
| The object is in the wrong state | IllegalStateException | none, we check a field |
| The operation is never supported | UnsupportedOperationException | none |
The JDK follows the first two rows. The Objects.requireNonNull()) method throws NullPointerException, and Objects.checkIndex() throws IndexOutOfBoundsException, so in code that follows the JDK a null argument or a bad index is not an IllegalArgumentException. An unmodifiable list rejects add() with UnsupportedOperationException.

5. Validating Arguments With Guard Clauses
A guard clause is an if check at the top of a method that throws before the method changes anything. Because the check runs first, the stack trace points at the caller that sent the bad value, not at a line deep inside our code.
The following example is a FruitBasket class with a fixed capacity, built on Java 25 with Spring Framework 7.0.9, Guava 33.7.2 and JUnit 6.1.3 tests (source on GitHub). Its add() method checks the arguments first and the basket state second, and get() checks the index.
public void add(String fruit, int quantity) {
Objects.requireNonNull(fruit, "fruit must not be null");
if (fruit.isBlank()) {
throw new IllegalArgumentException("fruit must not be blank");
}
if (quantity <= 0) {
throw new IllegalArgumentException("quantity must be positive: " + quantity);
}
if (closed) {
throw new IllegalStateException("basket is closed");
}
// add the fruits
}
public String get(int index) {
Objects.checkIndex(index, fruits.size());
return fruits.get(index);
}
Each message names the parameter and the bad value, so whoever reads the log knows the fix without opening the code. Each call below is a separate case.
FruitBasket basket = new FruitBasket(10);
basket.add("apple", -5); // IllegalArgumentException: quantity must be positive: -5
basket.add(null, 5); // NullPointerException: fruit must not be null
basket.add("apple", 3); // OK, size 3
String fruit = basket.get(5); // IndexOutOfBoundsException: Index 5 out of bounds for length 3
basket.close();
basket.add("apple", 5); // IllegalStateException: basket is closed
In a REST controller, we turn an IllegalArgumentException from a guard clause into a 400 response with an @ExceptionHandler method, and we test each guard clause with JUnit’s assertThrows().
5.1. Spring Assert and Guava Preconditions
Spring’s Assert class and Guava’s Preconditions class turn each guard clause into one line. Their state() and checkState() methods throw IllegalStateException. The two libraries differ on null checks. Spring’s Assert.notNull() throws IllegalArgumentException, whereas Guava’s checkNotNull() throws NullPointerException, like the JDK.
| Method | Exception thrown |
|---|---|
| Assert.isTrue(quantity > 0, “quantity must be positive”) | IllegalArgumentException |
| Assert.notNull(fruit, “fruit must not be null”) | IllegalArgumentException |
| Preconditions.checkArgument(quantity > 0, “quantity must be positive: %s”, quantity) | IllegalArgumentException |
| Preconditions.checkNotNull(fruit, “fruit must not be null”) | NullPointerException |
With quantity = -5, the Guava call fails with quantity must be positive: -5. We pick one library per project and use it everywhere, so the same kind of problem always gives the same exception type.
6. Catching IllegalArgumentException and Rejecting Null Arguments
We catch IllegalArgumentException only at the input boundary, and for null arguments we throw what the JDK throws, not what Spring throws.
6.1. Where Should We Catch IllegalArgumentException?
Only where outside input enters the app, such as a request parameter or a line from an uploaded file. There we catch it, as in section 2.3, and turn it into an error response or a default value. Inside our own code, an IllegalArgumentException means a bug in the caller, so we fix the caller instead of catching it, as the exception handling best practices also advise.
6.2. Should a Null Argument Throw NullPointerException or IllegalArgumentException?
Throw NullPointerException with Objects.requireNonNull(). The JDK and Guava both do it that way, and the Javadoc says requireNonNull() is “designed primarily for doing parameter validation in methods and constructors”. Only Spring’s Assert.notNull() throws IllegalArgumentException for null.
7. Conclusion
An IllegalArgumentException always points at a value. The message shows the bad value, and the first stack trace line in our own package shows where it was passed in, so we fix that line or the code that computed the value.
For JDK calls, we clean up names before valueOf() and check computed numbers before we pass them on. Parsing exceptions get caught once, where the input enters the app.
In our own methods, a wrong value gets an IllegalArgumentException and a wrong object state gets an IllegalStateException, while null arguments and bad indexes keep their own JDK exceptions.
8. References
- IllegalArgumentException Javadoc (Java 25)
- IllegalStateException Javadoc (Java 25)
- Objects Javadoc (Java 25)
- Enum.valueOf() Javadoc (Java 25))
- DateTimeException Javadoc (Java 25)
- Spring Framework Assert Javadoc
- Guava Preconditions Javadoc
Happy Learning !!