java.lang.StringIndexOutOfBoundsException は、文字列の長さの外の位置(インデックス)を指定したときの例外です。 位置は0から数えるので、長さ5の文字列で使える位置は0〜4です。Java 21 では、メッセージは2つの形のどちらかで出ます。

java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
java.lang.StringIndexOutOfBoundsException: Range [0, 6) out of bounds for length 5
メッセージの形 意味 主なメソッド
Index 〈i〉 out of bounds for length 〈n〉 1つの位置 i が 0〜n-1 の外 charAt・codePointAt・deleteCharAt・setCharAt
Range [〈開始〉, 〈終了〉) out of bounds for length 〈n〉 範囲が 0〜n の外、または開始 > 終了 substring・subSequence・delete・replace・insert
Range [〈開始〉, 〈開始〉 + 〈個数〉) out of bounds for length 〈n〉 配列の一部を指定する引数が外 String.valueOf(char[], 開始, 個数)・new String(char[], …)

直し方は、メッセージの数字(位置と length)を見て、「なぜその位置になったか」を突き止めることです。 多い原因は、indexOf が -1 を返した、空文字だった、文字数とバイト数を取り違えた、絵文字などで length() が見た目の文字数と違った、の4つです。

確認環境:OpenJDK 21.0.10(Linux)で、下に載せたメソッドのメッセージを実際に出して確かめました。版ごとの違いは、OpenJDK 1.8.0_504・11.0.32.1・17.0.20.1・21.0.10・25.0.4.1 で同じプログラムを動かして比べました(確認日 2026年10月12日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「StringIndexOutOfBoundsExceptionの直し方|原因一覧」

メッセージの読み方

Index の形(1つの位置)

Index 5 out of bounds for length 5

長さ(length)5 の文字列で、位置5を指定したという意味です。使える位置は0〜4なので、5は1つはみ出しています。「長さと同じ位置」を指定するのは、ループの条件を <= にした、length() をそのまま位置に使った、のどちらかが多いです。

Range の形(範囲)

Range [0, 6) out of bounds for length 5

[0, 6) は「0以上6未満」の範囲です。substring(開始, 終了) の終了は含まれないため、) で書かれています。範囲で使える条件は次のとおりです。

条件 例(length 5) 結果
0 ≦ 開始 ≦ 終了 ≦ 長さ substring(2, 5) OK("llo")
開始 = 終了 = 長さ substring(5) OK(空文字)
終了 > 長さ substring(2, 10) → Range [2, 10) 例外
開始 > 終了 substring(3, 1) → Range [3, 1) 例外
開始 < 0 substring(-1) → Range [-1, 5) 例外

substring(開始) は substring(開始, length()) として扱われます。 そのため "Hello".substring(6) は Range [6, 5) と、終了が5のメッセージになりました。

スタックトレースで、自分のコードの行を探す

Java 21 で "A-123".substring(0, 6) を実行したときのスタックトレースです。

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [0, 6) out of bounds for length 5
    at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
    at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
    at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
    at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112)
    at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349)
    at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4855)
    at java.base/java.lang.String.substring(String.java:2823)
    at Main.code(Main.java:2)
    at Main.main(Main.java:3)

java.base/ で始まる行は JDK の中なので飛ばし、最初に出る自分のクラスの行(Main.code(Main.java:2))が原因の行です。 その1つ上の行(String.substring)で、どのメソッドを呼んだかが分かります。Java 8 では JDK の中の行が1行だけでした。

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 6
    at java.lang.String.substring(String.java:1963)
    at Main.code(Main.java:2)
    at Main.main(Main.java:3)

メソッド別のメッセージ一覧(Java 21 で実行)

"Hello"(長さ5)などに、範囲外の位置を渡して出たメッセージです。

String のメソッド

呼び出し 例外 メッセージ
"Hello".charAt(5) SIOOBE Index 5 out of bounds for length 5
"Hello".charAt(-1) SIOOBE Index -1 out of bounds for length 5
"".charAt(0) SIOOBE Index 0 out of bounds for length 0
"Hello".substring(6) SIOOBE Range [6, 5) out of bounds for length 5
"Hello".substring(2, 10) SIOOBE Range [2, 10) out of bounds for length 5
"Hello".substring(3, 1) SIOOBE Range [3, 1) out of bounds for length 5
"Hello".subSequence(1, 9) SIOOBE Range [1, 9) out of bounds for length 5
"Hello".codePointAt(5) SIOOBE Index 5 out of bounds for length 5
"Hello".codePointBefore(0) SIOOBE Index -1 out of bounds for length 5
"Hello".getChars(0, 9, buf, 0) SIOOBE Range [0, 9) out of bounds for length 5
"Hello".offsetByCodePoints(0, 9) IOOBE null(メッセージなし)
String.valueOf(new char[3], 1, 5) SIOOBE Range [1, 1 + 5) out of bounds for length 3
new String(new char[3], 2, 5) SIOOBE Range [2, 2 + 5) out of bounds for length 3
new String(new byte[3], 0, 5, UTF_8) SIOOBE Range [0, 0 + 5) out of bounds for length 3

SIOOBE は StringIndexOutOfBoundsException、IOOBE は IndexOutOfBoundsException です。

StringBuilder のメソッド

呼び出し(中身は "Hello") 例外 メッセージ
sb.charAt(5) SIOOBE Index 5 out of bounds for length 5
sb.deleteCharAt(5) SIOOBE Index 5 out of bounds for length 5
sb.setCharAt(5, 'x') SIOOBE Index 5 out of bounds for length 5
sb.insert(6, "x") SIOOBE Range [6, 5) out of bounds for length 5
sb.substring(6) SIOOBE Range [6, 5) out of bounds for length 5
sb.delete(3, 1) SIOOBE Range [3, 1) out of bounds for length 5
sb.delete(6, 9) SIOOBE Range [6, 5) out of bounds for length 5
sb.replace(6, 7, "x") SIOOBE Range [6, 5) out of bounds for length 5
sb.setLength(-1) SIOOBE String index out of range: -1
new StringBuilder().append(new char[3], 1, 5) IOOBE Range [1, 6) out of bounds for length 3
new StringBuilder().append("abc", 1, 5) IOOBE Range [1, 5) out of bounds for length 3

例外にならないもの

呼び出し 結果 理由
"Hello".substring(5) ""(空文字) 開始=長さは範囲内
sb.delete(2, 99) "He" delete の終了は長さで切り詰められる
"Hello".indexOf('l', 9) -1 検索は範囲外でも例外にしない
"Hello".regionMatches(9, "x", 0, 1) false 範囲外は「一致しない」扱い

delete の終了は長すぎても例外になりませんが、substring の終了は例外になります。 同じ「範囲」の引数でも、メソッドによって扱いが違います。

Java の版ごとのメッセージの違い

同じプログラムを5つの JDK で動かした結果です。Java 17 から 21 の間で、「Index … out of bounds」「Range […) out of bounds」の形に揃いました。 25 は 21 と同じでした。

呼び出し Java 8 Java 11 Java 17 Java 21・25
"Hello".substring(6) String index out of range: -1 String index out of range: -1 begin 6, end 5, length 5 Range [6, 5) out of bounds for length 5
"Hello".substring(2, 10) String index out of range: 10 begin 2, end 10, length 5 begin 2, end 10, length 5 Range [2, 10) out of bounds for length 5
"Hello".substring(3, 1) String index out of range: -2 begin 3, end 1, length 5 begin 3, end 1, length 5 Range [3, 1) out of bounds for length 5
"Hello".charAt(5) String index out of range: 5 String index out of range: 5 String index out of range: 5 Index 5 out of bounds for length 5
"あいうえお".charAt(5) String index out of range: 5 index 5,length 5 index 5, length 5 Index 5 out of bounds for length 5
sb.deleteCharAt(5) String index out of range: 5 index 5,length 5 index 5, length 5 Index 5 out of bounds for length 5
sb.delete(3, 1) null start 3, end 1, length 5 start 3, end 1, length 5 Range [3, 1) out of bounds for length 5
sb.append(new char[3], 1, 5) ArrayIndexOutOfBoundsException: null IOOBE: start 1, end 6, length 3 IOOBE: start 1, end 6, length 3 IOOBE: Range [1, 6) out of bounds for length 3

読み取れることは3つです。

  • Java 8 の substring(6) の -1 は「位置 -1」ではありません。 「残りの長さ(5 - 6)」が -1 という意味でした。Java 8 のログを読むときは注意します。
  • Java 11・17 では、同じ charAt(5) でも、英数字だけの文字列と日本語を含む文字列でメッセージが違いました。 文字列の内部の持ち方(1バイトか2バイトか)で、通るコードが分かれていたためです。
  • 版を上げるとメッセージの文面が変わるため、ログ監視でメッセージの文字列を条件にしている場合は、条件を見直します。 例外のクラス名で判定すると版の影響を受けません。

原因別の直し方

原因 よくあるコード メッセージの特徴
indexOf が見つからず -1 s.substring(0, s.indexOf(",")) Range [0, -1)
文字列が短い・空 s.substring(s.length() - 3) Range [-1, 2)・length 0
ループの条件が <= for (i = 0; i <= s.length(); i++) s.charAt(i) Index n … for length n
バイト数を文字数として使った s.substring(0, s.getBytes(UTF_8).length) 終了が長さの数倍
別の文字列で求めた位置を使った 変換前の位置を変換後に使う 長さが変換前より短い
絵文字・𠮷 などで数がずれた 見た目の文字数で位置を計算 位置が1〜数個だけはみ出す

indexOf の -1 を確かめる

indexOf は、見つからないと -1 を返します。Java 21 での結果です。

String s = "abc";
s.substring(0, s.indexOf(","));        // Range [0, -1) out of bounds for length 3
s.substring(s.indexOf(":") + 1);       // "abc"(-1 + 1 = 0 なので例外にならない)

後ろの書き方は例外になりませんが、「見つからなかった」ことに気づけません。 見つからない場合をはっきり分けます。

int pos = s.indexOf(",");
String head = (pos >= 0) ? s.substring(0, pos) : s;   // 見つからなければ全体

短い文字列・空文字を先に分ける

"ab".substring("ab".length() - 3);    // Range [-1, 2) out of bounds for length 2
"".charAt(0);                         // Index 0 out of bounds for length 0
// 末尾3文字(短ければ全体)
String tail = s.length() <= 3 ? s : s.substring(s.length() - 3);

// 先頭1文字(空なら空)
String first = s.isEmpty() ? "" : s.substring(0, 1);

末尾の切り出しや substring の書き方の詳細は、関連記事「Java substringの使い方 末尾切り出しと範囲外エラーの直し方」で扱っています。null と空文字の判定は、関連記事「Java文字列の空判定|isEmptyとisBlankの違い早見表」で扱っています。

ループは < length() にする

for (int i = 0; i < s.length(); i++) {     // <= にすると最後に Index n … for length n
    char c = s.charAt(i);
}

文字を1つずつ見るだけなら、位置を使わない書き方にすると、この種の例外は起きません。

for (char c : s.toCharArray()) { … }
s.codePoints().forEach(cp -> { … });       // 絵文字も1文字として扱う(下で説明)

別の文字列で求めた位置を使わない

全角・半角の変換(正規化)や大文字・小文字の変換で、文字列の長さが変わることがあります。Java 21 で確かめた例です。

String kana = "ガギグ";                                       // 半角カナ+濁点で length 6
String norm = Normalizer.normalize(kana, Normalizer.Form.NFKC); // "ガギグ" で length 3
norm.charAt(kana.length() - 1);   // Index 5 out of bounds for length 3

位置は、実際に切り出す文字列で計算し直します。 変換と切り出しを同じ文字列に対して行うのが基本です。

全角・絵文字(サロゲートペア)でずれる

length() は「見た目の文字数」ではない

Java の String は UTF-16(1文字を2バイトの単位で表す方式)で文字を持ち、length() はその単位(char)の数を返します。絵文字や「𠮷」などは2つの char(サロゲートペア)で1文字になります。 Java 21 で数えた結果です。

文字列 length() コードポイント数 見た目の文字数(書記素) UTF-8 のバイト数
寿司 2 2 2 6
🍣寿司 4 3 3 10
𠮷野家 4 3 3 10
👨‍👩‍👧(家族の絵文字) 8 5 1 18
ガギ(半角カナ+濁点) 4 4 2 12
  • コードポイント:Unicode で1文字に付いた番号。codePointCount で数える
  • 書記素(グラフィーム):人が1文字と感じる単位。BreakIterator で数える

substring(0, 1) で絵文字を切ると、例外にはならず、壊れた半分の文字になります。 Java 21 では、"🍣寿司".substring(0, 1) は長さ1、中身は U+D83C(サロゲートペアの前半だけ)でした。例外にならない分、画面や DB で文字化けとして見つかることが多い問題です。

コードポイント単位で切り出す

// 先頭 n 文字(コードポイント単位)。短ければ全体を返す
static String leftCodePoints(String s, int n) {
    if (s.codePointCount(0, s.length()) <= n) {
        return s;
    }
    return s.substring(0, s.offsetByCodePoints(0, n));
}

leftCodePoints("🍣寿司が好き", 3);   // "🍣寿司"
leftCodePoints("ab", 3);             // "ab"
やりたいこと char 単位(ずれる) コードポイント単位
文字数 s.length() s.codePointCount(0, s.length())
i 番目の文字 s.charAt(i) s.codePointAt(s.offsetByCodePoints(0, i))
先頭 n 文字 s.substring(0, n) s.substring(0, s.offsetByCodePoints(0, n))
1文字ずつ s.toCharArray() s.codePoints()

offsetByCodePoints は、範囲を超えると IndexOutOfBoundsException(メッセージなし)になりました。上のように、先に codePointCount で長さを確かめてから呼びます。

家族の絵文字(👨‍👩‍👧)のように、複数のコードポイントで1文字に見えるものまで1文字として扱うには、java.text.BreakIterator.getCharacterInstance() を使います。Java 21 では、👨‍👩‍👧 は1文字、ガギ は2文字と数えました。

サロゲートペアのしくみは、関連記事「UnicodeとUTF-8の違いとは?UTF-16・サロゲートペアまで実行結果で理解」で扱っています。

バイト数で切り詰めるとき

DB の列が「10バイトまで」のときに、getBytes().length を文字の位置として使うと例外になります。

String name = "山田太郎";                                // length 4、UTF-8 で 12 バイト
name.substring(0, name.getBytes(UTF_8).length - 2);     // Range [0, 10) out of bounds for length 4

バイト数で切るときは、1文字ずつバイト数を足していき、超える手前で止めます。

static String leftBytes(String s, int maxBytes) {
    StringBuilder sb = new StringBuilder();
    int bytes = 0;
    for (int i = 0; i < s.length(); ) {
        int cp = s.codePointAt(i);
        int len = new String(Character.toChars(cp)).getBytes(StandardCharsets.UTF_8).length;
        if (bytes + len > maxBytes) {
            break;
        }
        sb.appendCodePoint(cp);
        bytes += len;
        i += Character.charCount(cp);
    }
    return sb.toString();
}

leftBytes("山田太郎🍣", 10);   // "山田太"(9バイト)
leftBytes("🍣寿司", 5);        // "🍣"(4バイト)

コードポイント単位で進めるので、絵文字を半分で切ることもありません。MySQL で絵文字を保存するときのエラーは、関連記事「MySQL Incorrect string valueの直し方|絵文字」で扱っています。

catch するなら IndexOutOfBoundsException

StringIndexOutOfBoundsException は IndexOutOfBoundsException の子クラスです。上の一覧のとおり、StringBuilder.append(char[], …) や offsetByCodePoints は親の IndexOutOfBoundsException を投げるため、catch (StringIndexOutOfBoundsException e) では受け止められません。

例外 親 主に出る場所
StringIndexOutOfBoundsException IndexOutOfBoundsException String・StringBuilder の位置の指定
ArrayIndexOutOfBoundsException IndexOutOfBoundsException 配列の添字(Java 8 の sb.append(char[], …) もこれ)
IndexOutOfBoundsException RuntimeException List.get・StringBuilder.append(char[], …) など

ただし、範囲外の例外は「事前に確かめれば防げる」種類の例外です。try-catch で包むより、上の直し方で位置を正しく計算するほうが、原因が残りません。配列の場合は関連記事「Java ArrayIndexOutOfBoundsException原因と対処」、リストの場合は関連記事「Index 0 out of bounds for length 0の直し方」で扱っています。

よくある質問

Q. String index out of range: -1 と出ます。どこで -1 を渡したのか分かりません。 A. Java 8 の substring(開始) では、-1 は「残りの長さ」のことがあります。開始が長さより1大きいときにこの値になります。Java 17 以降なら begin 6, end 5 や Range [6, 5) のように開始と終了が両方出るので、版を確かめてから読み分けます。

Q. 本番だけで起きて、手元では起きません。 A. 入力の文字列が違う可能性が高いです。空文字、区切り文字が無い行、想定より短いコード、絵文字を含む名前などを、テストデータに足して確かめます。境界値の考え方は、関連記事「同値分割・境界値分析|テストケース表とpytestコード例で解説」で扱っています。

Q. 例外一覧の中で、この例外はどこに入りますか。 A. 実行時例外(RuntimeException)の一種で、throws の宣言は要りません。ほかの例外との関係は、関連記事「Java 例外一覧|種類・原因・対処の早見表と使い分け」で扱っています。

まとめ

  • 文字列の長さの外の位置を指定したときの例外。位置は0〜長さ-1、範囲の終了は長さまで
  • Java 21 のメッセージは Index i out of bounds for length n と Range [開始, 終了) out of bounds for length n の2形式
  • Java 8・11・17 では文面が違う。Java 8 の -1 は「残りの長さ」のことがある
  • 多い原因は indexOf の -1、空・短い文字列、<= のループ、バイト数との取り違え
  • 絵文字・𠮷 は length() で2と数える。切り出しは offsetByCodePoints・codePoints() で
  • StringBuilder.append(char[], …) などは 親の IndexOutOfBoundsException を投げる

参考・出典

確認日はいずれも 2026年10月12日です。

  • Java SE 21 API「StringIndexOutOfBoundsException」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringIndexOutOfBoundsException.html
  • Java SE 21 API「String」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html
  • Java SE 21 API「StringBuilder」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuilder.html
  • Java SE 21 API「Character」(Unicode Character Representations):https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html
  • Java SE 21 API「BreakIterator」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/BreakIterator.html
  • OpenJDK jdk21u ソースコード「String.java」:https://github.com/openjdk/jdk21u/blob/master/src/java.base/share/classes/java/lang/String.java

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「StringIndexOutOfBoundsExceptionの直し方|原因一覧」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?