Repository navigation
scroll top reaches the top of Android Settings, then fails with scroll_edge_no_progress #3737
Description
Summary
On the Android Settings home screen, scroll top scrolls the list all the way back to the top, then fails after 3 passes with scroll_edge_no_progress. At the top, the nested RecyclerView still reports hiddenContentAbove: true. The edge loop keeps swiping up a list that can't move any further, the surface stays the same, and the stuck check fires. It happens after any downward scroll, not just after scroll bottom.
Environment
- agent-device
mainat 4db1b7d (built from source,node bin/agent-device.mjs) - Pixel_7_API_36 emulator (Android 16 / API 36, google_apis arm64-v8a), headless
- macOS, Apple silicon
Steps to reproduce
adb shell am force-stop com.google.android.settings.intelligence && adb shell am force-stop com.android.settingsagent-device open com.android.settings --platform androidagent-device scroll down --platform android --json(orscroll bottom)agent-device scroll top --platform android --json
Expected
Step 4 reports Scrolled to top with N up passes.
Actual
Step 3 succeeds. Step 4 fails, even though the screen is back at the top:
{
"code": "COMMAND_FAILED",
"message": "scroll top moved nothing across 3 passes: the container still reports hidden content but its contents never shifted",
"details": { "reason": "scroll_edge_no_progress", "edge": "top", "passes": 3, "dispatchedSteps": 3, "dispatched": "unknown" }
}Screenshots after open, after step 3 and after step 4. The final frame matches the freshly opened screen; in the scroll bottom run the two PNGs are byte-identical. The images and recordings are at the end.
snapshot --json at the top afterwards (scroll containers only):
| index | type | id | hiddenContentAbove | hiddenContentBelow |
|---|---|---|---|---|
| 11 | ScrollView |
settings_homepage_container |
– | – |
| 18 | ScrollView |
main_content_scrollable_container |
– | true |
| 23 | RecyclerView |
recycler_view |
true |
– |
It reproduced in every run: 4 of 4 on main (3 after scroll bottom, 1 after scroll down), and the same way on the #3736 branch, which doesn't change this path.
Not yet known
- Where the stale
hiddenContentAbovecomes from: the platform (canScrollVertically(-1)on aRecyclerViewnested in a scroll view) or agent-device's hint derivation. - Whether other apps with a
RecyclerViewinside aNestedScrollView, or other API levels, show it.
Done when
scroll top on this screen stops at the top and reports success. If the hint can't be trusted for nested scrollers, the edge loop shouldn't report no-progress for a list that visibly reached its edge.
Screenshots and recordings
In order: the scroll down → scroll top screenshots, the scroll bottom → scroll top screenshots, then a recording of each run in the same order.
scroll down → scroll top (after open, after scroll down, after the failed scroll top):
scroll bottom → scroll top (after open, after scroll bottom, after the failed scroll top):
Recording, scroll down → scroll top:
B-down-then-top.mp4
Recording, scroll bottom → scroll top:
Activity
airowe commentedon Oct 16, 2026
Root cause, from the tree on a freshly opened Settings screen that was never scrolled:
ScrollViewmain_content_scrollable_containertrueRecyclerViewrecycler_view(child of 39)trueThe flag is in
--rawoutput too, so it's set before any presentation filtering. It comes straight from the platform: the helper emitscan-scroll-backwardfrom the node'sACTION_SCROLL_BACKWARDaction (AccessibilityTreeXml.java#L89-L91), andandroidScrollActionHintsmaps it tohiddenContentAbove. The nestedRecyclerViewoffers scroll-backward even at the top, while its parentScrollView, with the identical rect, correctly offers only forward.scroll toppicks the RecyclerView, its swipes can't move anything, and the stuck check fires.Two possible fixes; I'd like your call before writing one:
scroll top/scroll bottom. It's narrow and only affects this nested setup.I lean toward 1. I can open a PR for whichever you prefer.
thymikee commentedon Oct 17, 2026
Thanks, the raw-tree root cause is clear. Go with option 1, scoped to the edge verdict. When a scrollable has the same rect as its scrollable ancestor,
scroll topandscroll bottomtake the edge hints from the ancestor. Leave the snapshot's reported hints unchanged. Add the Settings tree as the regression fixture.Option 2 would hide real content above in a RecyclerView. A PR is welcome.