Android Development
RecyclerView Error
Debugging Tips
Invalid Item Position
Mobile App Development

RecyclerView Inconsistency detected. Invalid item position

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

RecyclerView is efficient because it reuses view holders while your dataset changes underneath it. The crash Inconsistency detected. Invalid item position appears when the adapter and RecyclerView disagree about how many items exist or where they live. In practice, that usually means your list was mutated at the wrong time or the adapter sent the wrong notification.

Why the error happens

RecyclerView keeps internal bookkeeping for layout passes, animations, and scroll position. If your adapter reports one state while the backing list has already moved to another state, position lookups become invalid and the framework throws.

Typical causes include:

  • Changing the backing list from a background thread.
  • Removing or inserting items without the matching notify... call.
  • Calling notifyItemRemoved for one index while actually deleting a different element.
  • Reusing a mutable list after passing it to the adapter.
  • Mixing notifyDataSetChanged() with fine-grained notifications in the same update flow.

The fix is not a random try and catch. The fix is making updates atomic and predictable.

A safe pattern with ListAdapter

The easiest modern solution is to let DiffUtil compute item changes from immutable snapshots. ListAdapter handles the update bookkeeping for you and removes most manual notification bugs.

kotlin
1data class Message(val id: Long, val text: String)
2
3class MessageAdapter : ListAdapter<Message, MessageViewHolder>(DIFF) {
4    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
5        val view = LayoutInflater.from(parent.context)
6            .inflate(android.R.layout.simple_list_item_1, parent, false)
7        return MessageViewHolder(view)
8    }
9
10    override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
11        holder.bind(getItem(position))
12    }
13
14    companion object {
15        private val DIFF = object : DiffUtil.ItemCallback<Message>() {
16            override fun areItemsTheSame(oldItem: Message, newItem: Message): Boolean =
17                oldItem.id == newItem.id
18
19            override fun areContentsTheSame(oldItem: Message, newItem: Message): Boolean =
20                oldItem == newItem
21        }
22    }
23}
24
25fun renderMessages(adapter: MessageAdapter, incoming: List<Message>) {
26    adapter.submitList(incoming.toList())
27}

The important line is incoming.toList(). That creates a snapshot. If some other part of your code mutates the original collection later, the adapter still holds a stable copy.

Manual adapters need exact notifications

If you use RecyclerView.Adapter directly, the backing list and notification method must describe the same change in the same order.

kotlin
1class NamesAdapter(
2    private val items: MutableList<String>
3) : RecyclerView.Adapter<NameViewHolder>() {
4
5    override fun getItemCount(): Int = items.size
6
7    fun removeAt(position: Int) {
8        if (position !in items.indices) return
9        items.removeAt(position)
10        notifyItemRemoved(position)
11    }
12
13    fun insertAt(position: Int, value: String) {
14        if (position < 0 || position > items.size) return
15        items.add(position, value)
16        notifyItemInserted(position)
17    }
18}

Notice the sequence. First update the list, then send the matching adapter notification. If you remove the wrong element or call notifyItemInserted when the list size did not actually grow, RecyclerView will eventually catch the mismatch.

All adapter mutations should also happen on the main thread. If your data arrives in a worker thread, hop back before touching the list or adapter.

kotlin
lifecycleScope.launch(Dispatchers.Main) {
    adapter.submitList(repository.loadMessages().toList())
}

Debugging the source of the mismatch

When this crash is intermittent, add logging around every list mutation and every notify... call. You want to answer three questions:

  • Which thread changed the dataset
  • What the list size was before and after
  • Which adapter notification ran immediately after

If the counts do not line up, you have found the bug. If they do line up, inspect code that keeps multiple lists, such as a filtered list and a master list. Many crashes come from mutating one collection while getItemCount() reads another.

Common Pitfalls

Mutating adapter.currentList is a classic mistake with ListAdapter. Treat submitted lists as immutable snapshots, not shared state.

Another problem is dispatching updates after a fragment view is destroyed. The adapter may still receive data while the old RecyclerView is tearing down. Tie observers to the view lifecycle and remove callbacks when the UI goes away.

Disabling animations can make the crash appear less often, but it does not solve the data consistency bug. Use it only as a temporary diagnostic step.

Finally, avoid broad notifyDataSetChanged() calls as a bandage. They hide the exact update sequence and make it harder to reason about scroll position, animations, and state restoration.

Summary

  • The crash means adapter state and dataset state are out of sync.
  • Prefer ListAdapter with DiffUtil and immutable list snapshots.
  • For manual adapters, change the list and send the exact matching notification.
  • Keep adapter mutations on the main thread.
  • Log dataset size changes and notification calls to isolate intermittent bugs.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.