The fact that removeAll can actually remove children is very suspicious. To me 
that would indicate that the same ListView instance is being accessed by 
multiple threads.

How a stateless page can end up in another thread for another request, is 
beyond me.

Maybe add logging to removeAll for the thread name/ID, the object identity hash 
code and the number of children before the removal? Or throw an exception if 
the number of children is not 0 when removeAll is called?

Regards, Johan

Op donderdag 26 februari 2026 om 16:43 schreef Bas Gooren via users 
<[email protected]>:

> Hi all,
> 
> I have an interesting case, which I will admit I lost some sleep over 😉
> 
> The ListView API is not stable WRT its model.
> 
> We have not been able to reproduce this, but this is what we get in
> production:
> 
> Caused by: java.lang.NullPointerException: Cannot invoke
> "java.util.List.get(int)" because the return value of
> "org.apache.wicket.markup.html.list.ListView.getModelObject()" is null
> at
> org.apache.wicket.markup.html.list.ListItemModel.getObject(ListItemModel.java:55)
> ~[wicket-core-9.19.0.jar:9.19.0]
> at ....SectionsPanel.createSection(SectionsPanel.java:57) ~[-]
> at ....SectionsPanel$1.populateItem(SectionsPanel.java:48) ~[-]
> at
> org.apache.wicket.markup.html.list.ListView.onPopulate(ListView.java:523)
> ~[wicket-core-9.19.0.jar:9.19.0]
> at
> org.apache.wicket.markup.repeater.AbstractRepeater.onBeforeRender(AbstractRepeater.java:124)
> ~[wicket-core-9.19.0.jar:9.19.0]
> at org.apache.wicket.Component.beforeRender(Component.java:949)
> ~[wicket-core-9.19.0.jar:9.19.0]
> at
> org.apache.wicket.MarkupContainer.onBeforeRenderChildren(MarkupContainer.java:1759)
> ~[wicket-core-9.19.0.jar:9.19.0]
> 
> The simplest way to explain this:
> 
> We have a ListView in a stateless page.
> The ListView gets a list model mapped from an LDM on the page:
> 
> var parentModel = LoadableDetachableModel.of(() ->
> fetchItemFromCacheAndExternalApi());
> … = new ListView(“id”, parentModel.map(Parent::getItems)) { … }
> 
> For a reason unknown at this time, which only happens under high load, this
> stateless page is re-rendered.
> 
> On the re-render of the ListView, it walks through its onPopulate method.
> 
> First it calls getViewSize() to check if there are items to render (parent
> model is non-null and says: here’s a list of X items)
> Reuse items is off, so it calls removeAll => all children are removed and
> detached => which also detaches the list model and the page model
> It then assumes the “size” is still correct and starts creating and
> appending children (ListItem instances).
> Once those populate and call their ListItemModel.getObject(), the parent
> model reattaches and now returns null => Boom, there’s the NPE.
> 
> The reason that our page model returns non-null and null in quick
> succession is spam bots and thus cache issues; An external API we load data
> from also seems to misbehave and sometimes return empty results under load.
> This particular issue caught me by suprise, as normally a LDM is only
> loaded once during render cycle; Here it loads *twice*: for getViewSize()
> and then for the first ListItemModel, because it got detached by
> ListView#removeAll.
> 
> But more to the point: the ListView proceeds, after calling removeAll(), as
> if nothing can have changed. And that is not correct, the ListView model
> may have been detached by removing children and thus have a different value
> now.
> 
> Should ListView not re-calculate/query getViewSize() after calling
> removeAll()? That would remove this edge case?
> 
> Met vriendelijke groet,
> Kind regards,
> 
> Bas Gooren
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to