'Android Paging 3: LoadType.APPEND returns null remote keys
I've been trying to work out how to fix my issue with RemoteMediator's APPEND LoadType.
On an empty Room DB, here's how the LoadType flows:
REFRESH -> PREPEND -> APPEND (remoteKeys = null, endOfPaginationReached = true)
With at least 10 rows for entity and remote keys table, here's how the LoadType flows:
REFRESH -> PREPEND -> APPEND (remoteKeys = prev=null, next=2, endOfPaginationReached = false)
Clearly, my issue is on a fresh installed device (with empty Room DB), the user won't see more than 10 items because APPEND's state.lastItemOrNull() is returning null.
Here's my code so far:
private suspend fun getRemoteKeysForLastItem(state: PagingState<Int, MovieCache>): MovieRemoteKeys? {
return state.lastItemOrNull()?.let { movie ->
appDatabase.withTransaction {
appDatabase.remoteKeysDao().remoteKeysByImdbId(movie.imdbId)
}
}
}
for my load() function:
val loadKey = when (loadType) {
LoadType.REFRESH -> {
val key = getRemoteKeysClosestToCurrentPosition(state)
Timber.d("REFRESH key: $key, output: ${key?.nextKey?.minus(1)}")
key?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
Timber.d("PREPEND key requested")
return MediatorResult.Success(true)
}
LoadType.APPEND -> {
val key = getRemoteKeysForLastItem(state)
Timber.d("APPEND key: $key")
appDatabase.withTransaction {
val size = movieDao.movies().size
val remoteSize = remoteKeysDao.allKeys().size
Timber.d("APPEND DB size: $size, remote: $remoteSize")
}
key?.nextKey ?: return MediatorResult.Success(true)
}
}
Here's a sample logcat showing that APPEND is null

Leaving my app unable to scroll down even at least once!
Solution 1:[1]
Finally, this is how I managed to fix this issue, by relying on the remoteKeys on the DB than PagingState:
LoadType.APPEND -> {
// val key = getRemoteKeysForLastItem(state) // Doesn't work. state returns NULL even Room has data for both remote keys and entity.
val key = appDatabase.withTransaction {
remoteKeysDao.allKeys().lastOrNull() // Workaround
}
key?.nextKey ?: return MediatorResult.Success(true)
}
Solution 2:[2]
Instead of returning endOfPaginationReached = true from LoadType.APPEND when remote key is null, return endOfPaginationReached = key != null. Returning endOfPaginationReached = false means keep loading which will give you the remote key next time.
LoadType.APPEND -> {
val key = getRemoteKeysForLastItem(state)
Timber.d("APPEND key: $key")
appDatabase.withTransaction {
val size = movieDao.movies().size
val remoteSize = remoteKeysDao.allKeys().size
Timber.d("APPEND DB size: $size, remote: $remoteSize")
}
key?.nextKey ?: return MediatorResult.Success(key != null)
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Jim Ovejera |
| Solution 2 | Md. Asaduzzaman |

