From e909c4a6e250981948115ce5616415cd9d9316c5 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Fri, 16 Oct 2026 22:47:06 +0200 Subject: [PATCH] Iterate a copy of the project map in JobsView.expandAllProjects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit expandAllProjects iterated the key set of a synchronized map on the UI thread without holding its lock, while the refresh job clears and fills the map from worker threads, which can throw ConcurrentModificationException and abort Expand All. It now iterates a snapshot of the keys. Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../com/microsoft/copilot/eclipse/ui/jobs/views/JobsView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.copilot.eclipse.ui.jobs/src/com/microsoft/copilot/eclipse/ui/jobs/views/JobsView.java b/com.microsoft.copilot.eclipse.ui.jobs/src/com/microsoft/copilot/eclipse/ui/jobs/views/JobsView.java index a3352d73..bebd1f9b 100644 --- a/com.microsoft.copilot.eclipse.ui.jobs/src/com/microsoft/copilot/eclipse/ui/jobs/views/JobsView.java +++ b/com.microsoft.copilot.eclipse.ui.jobs/src/com/microsoft/copilot/eclipse/ui/jobs/views/JobsView.java @@ -396,8 +396,8 @@ private void handlePullRequestLoadError(String projectName, Throwable error) { * just expand them. For projects not yet loaded, load PRs first then expand. */ private void expandAllProjects() { - // Iterate through all projects in the tree - for (String projectName : repoToPullRequestsMap.keySet()) { + // Iterate over a copy: the refresh job clears and fills the map concurrently + for (String projectName : new ArrayList<>(repoToPullRequestsMap.keySet())) { // Skip special nodes like "No open projects" if (!projectNameToProjectMap.containsKey(projectName)) { continue;