GroupDataGrid rows render blank when scrolling inside expanded groups

Hello everyone.

I have a GroupDataGrid grouped by a text property (nombreGestorDeCuenta). The view loads approximately 1,200 records, with each group containing around 170 records.

When I expand a group, only the rows visible in the initial viewport render correctly. As soon as I scroll down past the initially visible rows, the remaining rows appear as blank lines — no data, no interaction, clicks don’t respond, and the UI becomes partially frozen.

If I scroll back up to the visible area and interact with another component (e.g., opening a ComboBox dropdown), the previously blank rows sometimes “wake up” and render. But if I interact while the blank rows are in the viewport, the component stays in a loading state and never completes.

Browser console errors

When scrolling into the blank area, I consistently get:

(NotFoundError): Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.

Full stack trace (key frames):

grid.$connector.flushParentRequests @ generated-flow-imports.js
grid.$connector.debounceParentRequest @ generated-flow-imports.js
grid.dataProvider @ generated-flow-imports.js
__loadCachePage @ indexhtml.js
ensureFlatIndexLoaded @ indexhtml.js
_getItem @ indexhtml.js
_updateScrollerItem @ indexhtml.js
_assignModels @ indexhtml.js
_scrollHandler @ indexhtml.js

Other related errors in the console:

  • Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'c') in FlowClient.js
  • Node id is not registered with this tree
  • Ignored listener invocation for item-double-click event from the client side for an inert vaadin-grid element

What I have already investigated and ruled out

  1. In-memory container manipulation: I was initially using getMutableItems().clear() / addAll() to implement a client-side filter. I removed this entirely and moved the filtering to the JPQL query in a PreLoadEvent handler. The problem persists.
  2. Cascading selection listeners: My view has two master grids (period selector and center group selector) that control what loads in the GroupDataGrid. The selection listener on the first grid calls setItems() on the second grid, which was triggering the second grid’s selection listener and causing double load() calls. I added a suppression flag to prevent this cascade. The problem persists.
  3. Concurrent loads: I centralized all load() calls through a single method with a guard flag to prevent overlapping loads. The problem persists.
  4. Deselecting before reload: I call deselectAll() on the GroupDataGrid before every load() to ensure the grid doesn’t hold references to stale items. The problem persists.
  5. Pagination / data volume: The problem occurs regardless of the itemsPerPageDefaultValue setting (tested with 100, 500, 1000). It fails consistently with any group that has more rows than fit in the visible viewport.
  6. Publicity filter (MultiSelectComboBox): I completely removed the filtering feature to rule it out. The problem persists.

Minimal reproduction scenario

  1. Select a period in the first DataGrid (loads ~1,200 detail records).
  2. Select a center group in the second DataGrid (triggers the load).
  3. The GroupDataGrid loads data grouped by nombreGestorDeCuenta.
  4. Expand any group that contains more items than fit in the viewport (~170 items).
  5. Scroll down → blank rows appear, insertBefore error fires in the console.

View XML

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<view xmlns="http://jmix.io/schema/flowui/view"
      xmlns:c="http://jmix.io/schema/flowui/jpql-condition"
      title="msg://detalleOfertaDeVentaListView.title"
      focusComponent="detalleOfertaDeVentasDataGrid" xmlns:groupg="http://jmix.io/schema/groupgrid/ui">
    <data>
        <collection id="detalleOfertaDeVentasDc"
                    class="es.hiperusera.athia.entity.DetalleOfertaDeVenta">
            <fetchPlan extends="_base">
                <property name="ofertaDeVenta" fetchPlan="_base"/>
                <property name="detOfVentaTipoPubli" fetchPlan="detOfVentaTipoPubli-fetch-plan"/>
                <property name="ofertaVentaOrganizacion" fetchPlan="_base"/>
                <property name="tipoOferta" fetchPlan="_base"/>
            </fetchPlan>
            <!-- FIX: Quitado readOnly="true" para evitar conflictos con la manipulación del container -->
            <loader id="detalleOfertaDeVentasDl">
                <query>
                    <![CDATA[select e from DetalleOfertaDeVenta e
                    where e.ofertaDeVenta = :ofertaDeVenta
                    and e.ofertaVentaOrganizacion = :ofertaVentaOrganizacion]]>
                </query>
            </loader>
        </collection>
        <collection id="ofertaDeVentaDc" class="es.hiperusera.athia.entity.OfertaDeVenta">
            <fetchPlan extends="_base"/>
            <loader id="ofertaDeVentaDl" readOnly="true">
                <query>
                    <![CDATA[select o from OfertaDeVenta o order by o.createdDate]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="false"/>
        <urlQueryParameters>
            <genericFilter component="genericFilter"/>
            <pagination component="pagination"/>
        </urlQueryParameters>
        <settings auto="true"/>
    </facets>
    <actions>
        <action id="selectAction" type="lookup_select"/>
        <action id="discardAction" type="lookup_discard"/>
    </actions>
    <layout>
        <hbox width="100%" boxSizing="CONTENT_BOX">
            <dataGrid id="ofertaDeVentaGrid" width="60em" maxHeight="14em" dataContainer="ofertaDeVentaDc">
                <columns>
                    <column property="descripcion" filterable="true" autoWidth="true" resizable="true"/>
                    <column property="fechaInicio" filterable="true" autoWidth="true" resizable="true"/>
                    <column property="fechaFin" filterable="true" autoWidth="true" resizable="true"/>
                    <column property="fechaEntrega" filterable="true" autoWidth="true" resizable="true"/>
                    <column property="fechaCierre" filterable="true" autoWidth="true" resizable="true"/>
                </columns>
            </dataGrid>
            <dataGrid id="grupoCentrosGrid" width="30em" maxHeight="14em" metaClass="OfertaVentaOrganizacion">
                <columns>
                    <column property="grupoCentros.descripcion" autoWidth="true"/>
                    <column property="ivaIncluido" autoWidth="true"/>
                </columns>
            </dataGrid>
        </hbox>
        <genericFilter id="genericFilter"
                       dataLoader="detalleOfertaDeVentasDl">
            <properties include=".*"/>
        </genericFilter>
        <vbox width="100%" padding="false" spacing="false">
            <hbox id="buttonsPanel" classNames="buttons-panel" alignItems="CENTER"
                  justifyContent="BETWEEN" width="100%">
                <hbox alignItems="CENTER" spacing="true">
                    <button id="createButton" action="detalleOfertaDeVentasDataGrid.createAction" enabled="false"/>
                    <button id="editButton" action="detalleOfertaDeVentasDataGrid.editAction"/>
                    <button id="bulkButton" action="detalleOfertaDeVentasDataGrid.bulkEditAction"/>
                    <button id="removeButton" action="detalleOfertaDeVentasDataGrid.removeAction"/>
                    <button id="excelBtn" action="detalleOfertaDeVentasDataGrid.excelExportAction"/>
                    <button id="exportPrecVentaSap" text="Exportar Precio Venta SAP" icon="vaadin:cloud-download" enabled="false"/>
                    <button id="duplicarBtn" text="Copiar" icon="vaadin:copy" action="detalleOfertaDeVentasDataGrid.copyAction"/>
                    <fileUploadField id="importacionExcelBtn" uploadIcon="vaadin:upload" acceptedFileTypes=".xlsx"
                                     uploadText="Importar Ofertas" enabled="false"/>
                    <gridColumnVisibility dataGrid="detalleOfertaDeVentasDataGrid" icon="COG" themeNames="icon"/>
                </hbox>
                <hbox alignItems="CENTER" spacing="true">
                    <multiSelectComboBox id="filtroTipoPubliCombo" label="Filtrar por publicidad" width="20em"/>
                    <simplePagination id="pagination" dataLoader="detalleOfertaDeVentasDl" itemsPerPageVisible="true"
                                      itemsPerPageItems="100, 500, 1000, 1500, 2000" itemsPerPageDefaultValue="100"/>
                </hbox>
            </hbox>
        </vbox>
        <groupg:groupDataGrid id="detalleOfertaDeVentasDataGrid"
                              dataContainer="detalleOfertaDeVentasDc"
                              minHeight="20em"
                              width="100%" columnReorderingAllowed="true" selectionMode="NONE">
            <groupg:actions>
                <groupg:action id="copyAction" type="list_itemTracking"/>
                <groupg:action id="createAction" type="list_create">
                    <groupg:properties>
                        <groupg:property name="openMode" value="DIALOG"/>
                    </groupg:properties>
                </groupg:action>
                <groupg:action id="editAction" type="list_edit">
                    <groupg:properties>
                        <groupg:property name="openMode" value="DIALOG"/>
                    </groupg:properties>
                </groupg:action>
                <groupg:action id="bulkEditAction" type="bulked_edit" text="Edición Masiva" >
                    <groupg:properties>
                        <groupg:property name="includeProperties" value="conjunto, codigoOferta"/>
                    </groupg:properties>
                </groupg:action>
                <groupg:action id="removeAction" type="list_remove"/>
                <groupg:action id="excelExportAction" type="grdexp_excelExport">
                    <groupg:properties>
                        <groupg:property name="columnsToExport" value="VISIBLE_COLUMNS"/>
                    </groupg:properties>
                </groupg:action>
            </groupg:actions>
            <groupg:groupBy>
                <groupg:columnRef key="nombreGestorDeCuenta"/>
            </groupg:groupBy>
            <groupg:columns>
                <groupg:groupColumn header="msg://groupColumn.header" key="nombreGestorDeCuentaHeader" resizable="true"/>
                <groupg:column property="nombreGestorDeCuenta" autoWidth="true" resizable="true"/>
                <groupg:column property="codigoArticulo" autoWidth="true" resizable="true"/>
                <groupg:column property="descripcion" filterable="true" resizable="true"/>
                <groupg:column property="conjunto" autoWidth="true" filterable="true" resizable="true"/>
                <groupg:column property="codigoOferta" autoWidth="true" filterable="true" resizable="true"/>
                <groupg:column property="tipoOferta" autoWidth="true" filterable="true" resizable="true"/>
                <groupg:column property="precioBase" autoWidth="true" filterable="true" resizable="true"/>
                <groupg:column property="precioBaseConIva" autoWidth="true" filterable="true" resizable="true"/>
                <groupg:column property="precioOfertaSimple" autoWidth="true" resizable="true"/>
                <groupg:column property="precioOfertaCompleja" autoWidth="true" resizable="true"/>
                <groupg:column property="precioUnitarioOfSimple" autoWidth="true" resizable="true"/>
                <groupg:column property="precioUnitarioOfCompleja" autoWidth="true" resizable="true"/>
                <groupg:column property="equivalenciaOfertaCompleja" autoWidth="true" resizable="true"/>
                <groupg:column property="equivalenciaOfertaSimple" autoWidth="true" resizable="true"/>
                <groupg:column property="porcentajeOferta" autoWidth="true" resizable="true"/>
                <groupg:column property="unidadesOferta" autoWidth="true" resizable="true"/>
                <groupg:column property="cartel" autoWidth="true" resizable="true"/>
                <groupg:column property="cabecera" autoWidth="true" resizable="true"/>
                <groupg:column property="precioOfertaSimpleConIva"/>
                <groupg:column property="precioOfertaComplejaConIva"/>
                <groupg:column property="precioUnitarioOfSimpleConIva"/>
                <groupg:column property="precioUnitarioOfComplejaConIva"/>
                <groupg:column property="observaciones"/>
            </groupg:columns>
        </groupg:groupDataGrid>
        <hbox id="lookupActions" visible="false">
            <button id="selectButton" action="selectAction"/>
            <button id="discardButton" action="discardAction"/>
        </hbox>
    </layout>
</view>

Important context on timing: This behavior is not entirely new — it has been occurring sporadically for a while, but very infrequently, and it was always resolved by simply refreshing the page (F5). However, in the last couple of days it has become a persistent, blocking issue: it now happens every single time I expand a group with many items, and refreshing the page no longer fixes it. No code changes were made between when it worked intermittently and when it became constant.

Environment

  • Jmix: 2.7.6
  • IntelliJ IDEA 2025.2.4 (Community Edition)
  • temurin-21.0.8

Any guidance would be appreciated. Thank you.