CLAUDE LABEN
FORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられていますFORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられています
記事一覧/Claude Code
Claude Code/2026-05-16中級

Claude Code でAndroid の RecyclerView クラッシュを28日間で根治した記録

Beautiful HD Wallpapersのv2.0.0リリース後、RecyclerViewのIndexOutOfBoundsExceptionが28日で50ユーザー以上に発生。Claude Codeとの対話で防御的コピーによる根治法を発見した実録。

android9kotlin3recyclerviewcrashdebugging5claude-code129

v2.0.0 をリリースした28日後、Firebase Crashlytics のダッシュボードに赤いグラフが現れました。

IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder — 50ユーザー以上が同じクラッシュで落ちています。Beautiful HD Wallpapers のリニューアルに自信を持っていた分、このエラーの根深さが際立ちました。

notifyDataSetChanged() を入れてみたり、DiffUtil に切り替えてみたりしました。表面的な対処を繰り返しても、Crashlytics のグラフは下がりませんでした。最終的に Claude Code との対話でたどり着いたのが「防御的コピー」です。診断から修正までのプロセスを、同じ問題で詰まっている方のために書き残しておきます。

RecyclerView の IndexOutOfBoundsException はなぜ厄介か

RecyclerView のこのエラーは、スタックトレースだけ見ると原因がわかりません。

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder
at androidx.recyclerview.widget.RecyclerView$Recycler.validateViewHolderForOffscreenChange

RecyclerView はデータソース(List<Wallpaper>)が外部から変更されていないという前提で動きます。アダプターへのデータ渡しと、バックグラウンドでのリスト更新が競合すると、RecyclerView 内部の「想定サイズ」と「実際のサイズ」がずれてクラッシュします。

問題は、このズレが常に再現するわけではないことです。マルチコア環境での微妙なタイミングで発生するため、ローカルでの再現が難しく、Crashlytics で見る頻度に対してデバッグが追いつかない状況が続きました。個人開発を2014年から続けてきた経験上、このタイプのバグは「ちゃんと書いた気がする」コードに潜んでいることが多く、最も時間がかかるカテゴリのひとつです。

Claude Code に診断を依頼したときの対話

壁紙アプリ Beautiful HD Wallpapers のコアとなる WallpaperListAdapter と、データを流す WallpaperRepository のコードを貼り付けて、Claude Code に診断を依頼しました。

わたし: RecyclerViewのIndexOutOfBoundsExceptionが本番で多発しています。
        スタックトレースと関連クラスを添付します。
        どのコードパスで発生しているか、診断してください。

Claude Code: WallpaperRepository.getWallpapers() が返す List<Wallpaper> を
            WallpaperListAdapter に直接渡している箇所を確認できます。
            Repository 内で同じリストオブジェクトへの参照が外部に渡されているため、
            バックグラウンドでのデータ更新時にアダプターが参照しているリストが
            変更されています。これが Inconsistency の根本原因です。

「同じリストオブジェクトへの参照」という一言で、問題の構造が明確になりました。

自分でデバッグしているときは「どこを直すか」を探してしまいます。Claude Code への相談を「どこが問題の構造なのか」という問いに変えたとき、_wallpaperList への参照漏れというアーキテクチャレベルの問題が浮かび上がりました。個人開発者にとって、コードレビューを依頼できる同僚がいないぶん、この種の診断対話は特に有用だと感じています。

私のコードでは、Repository が内部で保持する _wallpaperList をそのままアダプターに渡していました。ページング処理でバックグラウンドスレッドが _wallpaperList に追加操作をする間、RecyclerView の描画スレッドは「変更前のサイズ」で動いている — それがクラッシュの正体でした。

防御的コピーによる修正

Claude Code が提案した修正はシンプルでした。Repository がリストを返すとき、内部リストそのものではなく、その瞬間のスナップショットを返すようにするというものです。

修正前のコードでは、Repository が直接 _wallpaperList への参照を返していました。外部のアダプターはその参照を受け取るため、Repository 側でリストが変更されると、アダプターが持っているリストも意図せず変わってしまいます。

// 修正前: 内部リストへの参照がそのまま渡っていた
fun getWallpapers(): List<Wallpaper> {
    return _wallpaperList
}

修正後は toList() を使って防御的コピーを返します。

// 修正後: イミュータブルなスナップショットを返す
fun getWallpapers(): List<Wallpaper> {
    return _wallpaperList.toList()
}

toList() は Kotlin の標準関数で、元のリストの内容をコピーした新しいイミュータブルリストを作ります。アダプターが受け取るリストは「渡した瞬間の状態」で固定されるため、その後 Repository 側でいくら変更しても RecyclerView の内部状態とズレが起きません。

あわせて、アダプター側でもデータ更新時に明示的にコピーを取るよう変更しました。

class WallpaperListAdapter : RecyclerView.Adapter<WallpaperViewHolder>() {
    private var wallpapers: List<Wallpaper> = emptyList()
 
    fun submitList(newList: List<Wallpaper>) {
        // 防御的コピー+DiffUtilで差分更新
        val defensiveCopy = newList.toList()
        val diffResult = DiffUtil.calculateDiff(
            WallpaperDiffCallback(wallpapers, defensiveCopy)
        )
        wallpapers = defensiveCopy
        diffResult.dispatchUpdatesTo(this)
    }
 
    override fun getItemCount(): Int = wallpapers.size
}

DiffUtil と組み合わせることで、差分更新のアニメーションも維持しつつ、スレッドセーフな更新ができるようになりました。

v2.1.0 リリース後の結果

修正を含む v2.1.0 を段階公開(5% → 25% → 50% → 100%)でリリースしました。Crashlytics のグラフで確認できた変化は明確でした。IndexOutOfBoundsException によるクラッシュはゼロになり、Crash-free users は 99.8% で安定を維持しています。段階公開中にクラッシュ増加が見られなかったため、安心して 100% ロールアウトの判断ができました。

28日間悩んでいた問題が、コードの変更としては数行で解決しました。修正の規模が問題の深刻さに比例しないのが、こういった並行処理バグの特徴だと思います。

この問題が起きやすい他のパターン

防御的コピーが必要になる場面は他にもあります。StateFlow や LiveData を使っている場合でも、可変リストを直接 .value に代入すると同じ問題が起きます。

// ❌ よくある危険なパターン(StateFlow でも同じ問題が潜む)
class CategoryRepository {
    private val _categories = mutableListOf<Category>()
    private val _categoriesFlow = MutableStateFlow<List<Category>>(emptyList())
 
    fun update(newCategories: MutableList<Category>) {
        _categoriesFlow.value = newCategories  // 可変リストへの参照がそのまま流れる
    }
}
 
// ✅ 安全なパターン
class CategoryRepository {
    private val _categories = mutableListOf<Category>()
 
    // 防御的コピーを返す
    fun getCategories(): List<Category> = _categories.toList()
 
    // StateFlow の場合もコピーしてから代入
    private val _categoriesFlow = MutableStateFlow<List<Category>>(emptyList())
    val categoriesFlow: StateFlow<List<Category>> = _categoriesFlow.asStateFlow()
 
    fun update(newCategories: List<Category>) {
        _categoriesFlow.value = newCategories.toList()
    }
}

私が今実践しているルールは「リストをコンポーネントや別スレッドの境界をまたいで渡す前に、必ず .toList() を呼ぶ」です。習慣化してからは、同様のクラッシュが再発していません。RecyclerView 以外でも、ViewPager2 やカスタム View でデータリストを保持する場合は同様の注意が必要です。

次に書く予定のこと

Beautiful HD Wallpapers の Android 版は引き続き更新を続けています。次に書こうと思っているのは、Glide 5.0.5 と AGP 9.x の組み合わせで発生した Java 8 の互換性クラッシュです。こちらは coreLibraryDesugaringEnabled true の1行追加で解決しましたが、その原因にたどり着くまでの経緯が今回と同じくらい面白かったので、また整理して書きます。

もし同じ IndexOutOfBoundsException で詰まっている方がいれば、まず Repository からアダプターへの「参照の渡し方」を確認してみてください。修正は小さいはずです。デバッグの時間より、実装の楽しさに集中できる時間が増えることを願っています。ViewPager2 やカスタム View でも同様の参照漏れは起こりえます。Android 開発で並行処理由来のクラッシュに直面したとき、「防御的コピー」という選択肢を思い出していただければ幸いです。

Claude Code を使ったデバッグのアプローチについて

今回改めて感じたのは、「コードを貼って質問する」だけでなく「診断を依頼する」というアプローチの有効性です。自分でデバッグしているときは「どこを直すか」を探してしまいがちです。Claude Code への問いを「どこが問題の構造なのか」に変えたとき、表面的な修正候補ではなく、アーキテクチャレベルの問題が見えてきました。

AdMob 広告を収益源に個人開発を2014年ごろから続けています。Beautiful HD Wallpapers をはじめとした壁紙アプリ群は累計5,000万DLを超えており、一人でバグ対応から収益管理まで担っています。こういった並行処理由来のバグに限らず、「自分では見えていなかった問題の構造」を発見するために他者の視点が必要になる場面は何度もありました。10年前なら信頼できる同僚にレビューを頼む場面です。今は Claude Code に「診断してください」と投げることで、同様の視点を得られます。

問いの立て方さえ変えれば、Claude Code はコードレビュアーとして機能します。「このコードを直して」ではなく「このコードの問題の構造を教えて」と問いかけることで、根本原因にたどり着く速度が変わりました。私自身、この方法に切り替えてから、同種の問題に費やす時間が大幅に減ったと感じています。

問いの立て方さえ変えれば、Claude Code はコードレビュアーとして機能します。「このコードを直して」ではなく「このコードの問題の構造を教えて」と問いかけることで、根本原因にたどり着く速度が大きく変わりました。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Claude Code2026-06-12
Android でペイウォールとレビュー誘導が重なって表示される競合を ModalGate で解決した実装メモ
ペイウォール・レビュー誘導・リワード広告案内の3つのダイアログが同時に表示される競合を、優先度付きの中央ゲート(ModalGate)で解決した実装メモです。Claude Code とのレビューで見つかった閉じ漏れの3経路と Kotlin 実装例を紹介します。
Claude Code2026-06-12
Android の戻るボタン広告ゲートを入れ子から並列独立構造へ — Claude Code と進めた終了フロー再設計の記録
戻るボタンの広告が「出ない」と「出すぎる」を同時に起こした原因は、入れ子分岐に埋め込まれた暗黙の優先度でした。壁紙アプリ v2.1.0 で広告ゲートを並列独立構造へ作り直した過程を、Kotlin実装・Claude Codeへの依頼内容・テストまで含めて記録します。
Claude Code2026-05-27
Claude Code で Glide 5.0.5 の Java 8 API クラッシュを1行で消した話
Beautiful HD Wallpapers v2.0.0 で Android 6.0.1 ユーザーが全員 3 秒以内にクラッシュ。Claude Code との対話で coreLibraryDesugaringEnabled の 1 行だけが足りていなかったことに辿り着いた実体験です。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →