java.lang.IllegalStateException: What It Is, Why It Happens, and How to Fix It
Hitting java.lang.IllegalStateException in Java or Android? This guide explains exactly what triggers it, covers every common scenario, and gives you practical fixes with real code examples.
If you’ve written Java for more than a few weeks, there’s a good chance you’ve stared at this line in a stack trace:
java.lang.IllegalStateException
Sometimes it comes with a helpful message. Sometimes it doesn’t. Either way, java.lang.IllegalStateException tells you the same thing: you called a method at the wrong time, when the object or environment wasn’t in the right state to handle it. The fix isn’t always obvious, but the root cause follows a clear pattern across almost every scenario.
This post covers what the exception means, the most common places it shows up, and how to resolve it in each case.
What IllegalStateException Actually Means
IllegalStateException is an unchecked runtime exception in Java. It extends RuntimeException, which means it doesn’t need to be declared in a method signature and won’t be caught unless you explicitly catch it.
According to the Java documentation, it signals “that a method has been invoked at an illegal or inappropriate time.” The key phrase is inappropriate time. The method you called might be perfectly valid in other circumstances, but right now, in this particular state, the object doesn’t support it.
This is different from IllegalArgumentException, which tells you the argument passed was wrong. IllegalStateException tells you the object itself is in the wrong state for the operation you’re attempting.
It’s an unchecked exception that both the Java runtime and your own code can throw intentionally. When you want to enforce object lifecycle rules in your own classes — for example, preventing a method from being called before initialization completes — IllegalStateException is exactly the right exception to throw.
Common Causes and Fixes
1. Iterator.remove() Called Before Iterator.next()
This is one of the most frequent triggers in standard Java. When you iterate over a collection with an Iterator and call remove() before calling next(), you get:
java.lang.IllegalStateException
at java.util.ArrayList$Itr.remove(ArrayList.java:980)
The iterator’s cursor isn’t pointing at any element yet, so there’s nothing to remove. Here’s the broken code:
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Carol"));
Iterator<String> it = names.iterator();
it.remove(); // IllegalStateException — next() was never called
The fix: always call next() first to advance the cursor, then call remove():
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next(); // advance first
if (name.equals("Bob")) {
it.remove(); // now safe to remove
}
}
2. Thread.start() Called on an Already-Running Thread
Once a thread has been started in Java, you cannot start it again. Calling Thread.start() a second time on the same thread instance throws IllegalStateException.
Thread t = new Thread(() -> System.out.println("Running"));
t.start();
t.start(); // IllegalStateException — thread already started
The fix: create a new Thread instance if you need to run the task again. Threads are not reusable objects. If you need repeatable execution, use an ExecutorService instead:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("Running"));
executor.submit(() -> System.out.println("Running again")); // Fine
3. Adding an Element to a Full Queue
Bounded queues like ArrayBlockingQueue throw IllegalStateException when you try to add more elements than their capacity allows:
Queue<Integer> queue = new ArrayBlockingQueue<>(2);
queue.add(1);
queue.add(2);
queue.add(3); // IllegalStateException: Queue full
The fix: use offer() instead of add(). The offer() method returns false when the queue is full rather than throwing an exception:
boolean added = queue.offer(3);
if (!added) {
System.out.println("Queue is full, element not added");
}
4. Scanner Closed Before Being Read
If you close a Scanner prematurely and then try to read from it, or if you create a Scanner wrapping System.in and something closes it, you’ll get IllegalStateException: Scanner closed.
Scanner scanner = new Scanner(System.in);
scanner.close();
String input = scanner.nextLine(); // IllegalStateException: Scanner closed
The fix: don’t close a Scanner that’s wrapping System.in in the middle of your program. If you need to close resources, do it only when you’re done with all input. Use try-with-resources when working with file-based scanners, and leave the System.in scanner open until the program exits.
5. JVM Shutdown Hook Registration After Shutdown Has Begun
Once the JVM shutdown sequence starts, you cannot register new shutdown hooks or de-register existing ones. Attempting either operation throws IllegalStateException. This is a designed safeguard — the JVM won’t allow you to modify its shutdown behavior mid-process.
The fix is to register all shutdown hooks during application startup, before any shutdown-triggering event can occur.
IllegalStateException in Android
Android adds its own scenarios on top of standard Java. Fragment lifecycle management is the biggest source of IllegalStateException in Android development.
Fragment Transactions After onSaveInstanceState
The most common Android IllegalStateException message is:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
This happens when you try to commit a fragment transaction after the activity has saved its state. Android saves state before entering the background, and any fragment commits after that point would be lost on restore, so the system prevents them.
The fix: check whether state has been saved before committing:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container, new MyFragment());
if (!getSupportFragmentManager().isStateSaved()) {
transaction.commit();
} else {
transaction.commitAllowingStateLoss(); // Use with caution
}
Use commitAllowingStateLoss() only when losing the UI state from that transaction is acceptable. For example, if you’re showing a dialog that isn’t critical to the user’s workflow. Avoid it when the transaction involves saving meaningful user data.
The best long-term fix is restructuring your code so fragment transactions only happen in lifecycle methods where the state hasn’t been saved yet: onCreate(), onStart(), and onResume().
Duplicate Fragment Addition
If you try to add a fragment that’s already attached to the activity, you’ll get:
java.lang.IllegalStateException: Fragment already added
Check before adding:
if (!myFragment.isAdded()) {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.container, myFragment)
.commit();
}
UI Updates on the Wrong Thread
Android requires all UI updates to happen on the main thread. Updating a view from a background thread throws IllegalStateException: Only the original thread that created a view hierarchy can touch its views.
Use runOnUiThread() or a Handler to post UI work back to the main thread:
runOnUiThread(() -> {
myTextView.setText("Updated");
});
IllegalStateException in Spring and Spring Boot
Spring applications throw IllegalStateException in several common situations:
- ApplicationContext not initialized: calling
getBean()before the context has fully started - Using a closed EntityManager or Session: trying to execute a query after the persistence context has been closed
- HTTP response already committed: attempting to write headers or call
sendRedirect()after the response has already been sent to the client
In Spring MVC, the “response already committed” scenario looks like this in the logs:
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
This happens when you write to the response body and then try to redirect. Once the response starts writing to the output stream, headers are locked. Structure your controllers so the redirect or forward decision happens before any content is written.
Understanding what “state” means in a framework context is essential, whether you’re building Java backends or managing large data workflows. Knowing which lifecycle phase your code runs in prevents most IllegalStateException occurrences before they happen. This kind of disciplined thinking about program state also maps to how high-value data careers approach data pipeline reliability, covered in Top 10 Big Data Careers: Highest Paying Jobs on DataWider.
How to Throw IllegalStateException in Your Own Code
When you design classes with internal state requirements, IllegalStateException is the right exception to throw when a caller tries to use your object in an invalid state:
public class Connection {
private boolean connected = false;
public void connect() {
connected = true;
}
public void sendData(String data) {
if (!connected) {
throw new IllegalStateException("Cannot send data: not connected");
}
// send logic
}
}
This gives callers a clear, actionable error message rather than a NullPointerException or silent failure. Always include a message that explains which state precondition was violated. Testing these preconditions properly across environments is part of why organizations invest in rigorous software quality practices, as discussed in Software Testing at Google on DataWider.
Debugging IllegalStateException: A Quick Checklist
When you hit java.lang.IllegalStateException, run through this:
- Read the full message. The text after the colon usually tells you exactly what state rule was violated.
- Check the stack trace. Find the first line in your own code — that’s where the problematic call happened.
- Think about lifecycle. Was the method called before initialization? After disposal? After a thread started or stopped?
- Check for threading issues. UI updates on background threads in Android, or concurrent access to non-thread-safe collections in Java.
- In Android: look for fragment transactions happening outside of valid lifecycle states.
- In Spring: check whether the application context is fully loaded before accessing beans.
Browser and cross-platform testing practices also apply to Java applications, where ensuring correct behavior across environments matters as much as in web apps. Top 10 Browser Compatibility Testing Tools from DataWider illustrates how systematic environment-checking prevents runtime surprises.
Key Takeaways
java.lang.IllegalStateException means a method was called when the object or environment wasn’t ready for it. Here’s what to remember:
- Call
Iterator.next()beforeIterator.remove() - Don’t call
Thread.start()more than once on the same instance; useExecutorServicefor repeatable tasks - Use
Queue.offer()instead ofQueue.add()on bounded queues - In Android, commit fragment transactions only in valid lifecycle states and check
isStateSaved()before callingcommit() - In Spring, don’t write to the response before deciding whether to redirect
- Throw
IllegalStateExceptionin your own classes to enforce preconditions clearly
The exception is a signal about sequence and timing. Fix the sequence, and the exception goes away.