Vaadin 24 grid listener does not work after grid.getDataProvider().refreshAll()

So this is my Contact Information editor.
Somehow after editing an existing entry, i can no longer select it inside the grid.
Well i can select it, but the selectionListener does not fire an event anymore.

But when adding a new entry, the selection works as expected.

Also i am getting a debug log message

2025-07-18T10:35:51.637+02:00 DEBUG 74661 --- [nio-8080-exec-7] com.vaadin.flow.component.grid.Grid      : Key not found: 7. This can happen due to user action while changing the data provider.

Is this a known bug with the Grid?

LMK if you need more code or information.

H3 contactInformationHeader = new H3("Contact Information");
form.add(contactInformationHeader, 1);
	
Grid<ContactInformationDto> grid = new ThemedGrid<>(ContactInformationDto.class, false);
	
grid.addColumn(ContactInformationDto::getName).setHeader("Name");
grid.addColumn(ContactInformationDto::getValue).setHeader("Value");
	
List<ContactInformationDto> list = new ArrayList<>(person.getContactInformation());
	
binder.addStatusChangeListener(event -> {
	list.clear();
	list.addAll(person.getContactInformation());
});
	
Binder<ContactInformationDto> ciBinder = new Binder<>(ContactInformationDto.class);
ciBinder.setBean(new ContactInformationDto());
	
	
ListDataProvider<ContactInformationDto> dataProvider = new ListDataProvider<>(list);
dataProvider.setSortOrder(ContactInformationDto::getName, SortDirection.ASCENDING);
grid.setDataProvider(dataProvider);
	
grid.addSelectionListener(event -> {
	Optional<ContactInformationDto> optional = event.getFirstSelectedItem();
	if (optional.isPresent()) {
		ciBinder.setBean(optional.get());
	} else {
		ciBinder.setBean(new ContactInformationDto());
	}
});
	
TextField nameField = new TextField("Name");
ciBinder.forField(nameField)
		.asRequired()
		.bind(ContactInformationDto::getName, ContactInformationDto::setName);
	
TextField valueField = new TextField("Value");
ciBinder.forField(valueField)
		.asRequired()
		.bind(ContactInformationDto::getValue, ContactInformationDto::setValue);
	
Button addButton = new Button("Add", VaadinIcon.PLUS.create());
addButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
addButton.addClickListener(event -> {
	if (ciBinder.isValid()) {
		ContactInformationDto bean = ciBinder.getBean();
		if (bean.getId() == null) {
			list.add(bean);
		}
		grid.getDataProvider().refreshAll();
		grid.deselectAll();
		ciBinder.setBean(new ContactInformationDto());
		person.setContactInformation(new HashSet<>(list));
	}
});
	
Button removeButton = new Button("Remove", VaadinIcon.MINUS.create());
removeButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_PRIMARY);
	
HorizontalLayout inputLayout = new HorizontalLayout(nameField, valueField, addButton, removeButton);
inputLayout.setFlexGrow(1, nameField, valueField);
inputLayout.setWidthFull();
inputLayout.setAlignItems(Alignment.END);
	
VerticalLayout contactInformationLayout = new VerticalLayout(grid, inputLayout);
contactInformationLayout.setPadding(false);

Two first assumptions, what might be potential issues here:

  1. the binder just resets the list, but does not call refreshAll on the grid.
  2. does ContactInformationDto implement equals/hashCode? if yes, how? If not, then this might be also a reason.

Beside of that:
What does binder do or where does that come from or what is the intention of it?

This is the code for ContactInformationDto, i have tried with and without equals and hashCode, both cause the same result.

“the binder just resets the list, but does not call refreshAll on the grid.”
→ I have added this, but this does not solve the selectionListener issue

package ch.orca.data.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.io.Serializable;

/**
 * DTO for {@link ch.orca.data.entity.ContactInformation}
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ContactInformationDto implements Serializable {
	
	private Long id;
	
	private String name;
	
	private String value;
	
	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		
		if (!(o instanceof ContactInformationDto that)) return false;
		
		return new EqualsBuilder().append(id, that.id).append(name, that.name).append(value, that.value).isEquals();
	}
	
	@Override
	public int hashCode() {
		return new HashCodeBuilder(17, 37).append(id).append(name).append(value).toHashCode();
	}
}

Have you tried with equals/hash based on the id only? Since you change name / value, the equals/hash will also change, making it a different object for the grid.

Thanks, this works and the selectionListener event is now fired again, but when adding new items, they don’t have an id, which is correct, but this again scrambles the hashCode, because every object with no id, will have the same hashCode

completely removing hashCode, and @EqualsAndHashcode (via @Data) did the trick