The StringIndexOutOfBoundsException means a Java program tried to access a character in a String using an index that doesn’t exist. This usually happens when the program assumes a string has a certain length or structure that it doesn’t actually possess, leading to an invalid index calculation.

Here are the common culprits and how to fix them:

1. Off-by-One Error in Loops: This is the most frequent cause. You might be iterating up to string.length() (inclusive) instead of string.length() - 1 (inclusive), or using string.length() as the index itself.

  • Diagnosis: Carefully review for loops that access characters using string.charAt(i) or string.substring(i, j). Look for conditions like i <= string.length() or j == string.length().
  • Fix: Ensure your loop conditions are i < string.length() for accessing individual characters, or that substring end indices are within bounds. For example, if you want to get the last character, use string.charAt(string.length() - 1).
  • Why it works: String indices are zero-based, meaning the first character is at index 0 and the last is at length() - 1. Going beyond length() - 1 is out of bounds.

2. Empty or Null Strings: Attempting to get a character or substring from an empty string ("") or a null string will cause this exception. null strings don’t have a length or characters to access.

  • Diagnosis: Before accessing string content, add checks: if (myString == null || myString.isEmpty()).
  • Fix: Add explicit checks for null or empty strings and handle them appropriately (e.g., return a default value, log an error, or skip processing).
    String myString = getSomeString();
    if (myString != null && !myString.isEmpty()) {
        char firstChar = myString.charAt(0);
        // ... process string ...
    } else {
        // Handle null or empty string case
    }
    
  • Why it works: This prevents the program from even attempting to access an index on a non-existent string or an empty one, which has no valid indices.

3. Incorrect Substring Index Calculation: When using string.substring(startIndex, endIndex), both startIndex and endIndex must be valid and startIndex must be less than or equal to endIndex. Common mistakes include endIndex being equal to string.length() when you expect it to be the index of the last character, or startIndex being greater than endIndex.

  • Diagnosis: Examine calls to substring(). Pay close attention to how startIndex and endIndex are derived, especially if they involve calculations based on string content or external input.
  • Fix: Ensure startIndex is between 0 and string.length(), and endIndex is between startIndex and string.length(). Remember substring(start, end) includes characters from start up to (but not including) end.
    String data = "12345";
    // To get "234" (characters at index 1, 2, 3)
    String sub = data.substring(1, 4); // Correct: endIndex is 4, which is length of "12345"
    // Incorrect: data.substring(1, 5) would get "2345", data.substring(1, 3) gets "23"
    
  • Why it works: substring’s endIndex is exclusive. If you want characters up to index k, endIndex must be k + 1. If endIndex equals string.length(), it correctly extracts characters from startIndex to the very end of the string.

4. Parsing Malformed Input: If your string is supposed to contain structured data (like numbers, dates, or delimited fields), and the input doesn’t conform to the expected format, parsing logic can lead to invalid index calculations. For example, splitting a string and then trying to access an element that doesn’t exist.

  • Diagnosis: If the exception occurs after parsing or splitting a string, check the format of the input string that caused the problem. Use debugging to inspect the string’s actual content and length.
  • Fix: Implement robust input validation and error handling. Before accessing parts of a parsed string, check the number of elements obtained.
    String line = "user,id"; // Expected: "user,id,status"
    String[] parts = line.split(",");
    if (parts.length > 2) { // Check if the expected index exists
        String status = parts[2];
        // ...
    } else {
        System.err.println("Malformed line: " + line);
    }
    
  • Why it works: This ensures you only attempt to access elements of an array or parts of a string if they actually exist, preventing ArrayIndexOutOfBoundsException or StringIndexOutOfBoundsException stemming from parsing errors.

5. Incorrect Index Calculations with String Manipulation: When you repeatedly modify or build strings, especially in loops, and then try to extract parts based on the final state, intermediate steps might have altered lengths unexpectedly.

  • Diagnosis: If the exception happens after a series of string concatenations, replacements, or other modifications, trace the string’s length and content at each step.
  • Fix: Re-evaluate the logic. It’s often safer to calculate indices based on the original string or to use a StringBuilder and manage its length carefully.
    String original = "abcdef";
    String modified = original.replace("c", ""); // modified is "abdef", length 5
    // If you then try to access original.charAt(5) after assuming length 6, it's fine.
    // But if you tried to use a calculated index based on a *different* modification,
    // it could fail. Always base index access on the *current* string's actual length.
    
  • Why it works: By always referencing the length() of the string at the moment of access, you ensure index calculations are always valid for the current string state.

6. Regular Expression Group Indexing: If you’re using Matcher.group(index) after a regular expression match, and the group index you’re requesting doesn’t exist in the pattern or wasn’t captured by the match, you’ll get an IndexOutOfBoundsException.

  • Diagnosis: Inspect the regular expression pattern and the groupCount() of the Matcher object.
  • Fix: Ensure the group number you’re requesting is less than or equal to the number of capturing groups defined in your regex, and that the group was actually matched. Use matcher.groupCount() to know how many groups are available.
    Pattern pattern = Pattern.compile("(\\d+)-(\\d+)"); // Two groups
    Matcher matcher = pattern.matcher("123-456");
    if (matcher.find()) {
        String group1 = matcher.group(1); // Valid
        String group2 = matcher.group(2); // Valid
        // String group3 = matcher.group(3); // Throws IndexOutOfBoundsException
    }
    
  • Why it works: group(index) corresponds to the index-th capturing parenthesis in the regex. If that group wasn’t defined or matched, the index is out of bounds for the available groups.

The next error you’ll likely encounter after fixing these is a NullPointerException, usually because you’ve handled the StringIndexOutOfBoundsException cases by returning null and haven’t checked for that null return value downstream.

Want structured learning?

Take the full Java course →