{"record":{"id":"efcabef589dbb627","repo":"elastic/elasticsearch","slug":"classname-does-not-support-retainall","errorCode":null,"errorMessage":"${className} does not support retainAll()","messagePattern":"(.+?) does not support retainAll\\(\\)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/LazyPropertyList.java","lineNumber":108,"sourceCode":"    }\n\n    @Override\n    public boolean addAll(int index, Collection<? extends T> c) {\n        int i = index;\n        for (T item : c) {\n            this.add(i++, item);\n        }\n        return true;\n    }\n\n    @Override\n    public boolean removeAll(Collection<?> c) {\n        throw new UnsupportedOperationException(this.getClass().getName() + \" does not support removeAll()\");\n    }\n\n    @Override\n    public boolean retainAll(Collection<?> c) {\n        throw new UnsupportedOperationException(this.getClass().getName() + \" does not support retainAll()\");\n    }\n\n    @Override\n    public void clear() {\n        delegate.clear();\n    }\n\n    @Override\n    public T get(int index) {\n        PropertyListEntry<T> entry = delegate.get(index);\n        validate(entry);\n        return entry.getValue();\n    }\n\n    @Override\n    public T set(int index, T element) {\n        return delegate.set(index, new PropertyListEntry<>(() -> element, PropertyNormalization.DEFAULT)).getValue();\n    }","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/LazyPropertyList.java#L90-L126","documentation":"LazyPropertyList overrides List.retainAll(Collection) to throw UnsupportedOperationException. retainAll is the complement of removeAll and would force eager evaluation of every lazy supplier to compute the intersection, defeating the deferred-evaluation design. clear() is supported as a full reset.","triggerScenarios":"Calling .retainAll(Collection) on a LazyPropertyList. Surfaces when intersecting lists, deduplicating against an allow-list, or using collection frameworks that invoke retainAll for filtering.","commonSituations":"Migrating intersection/dedup logic from ArrayList to LazyPropertyList; Gradle task code that narrows a configured list to a subset at configuration time.","solutions":["Compute the retained subset into a new list, clear(), then re-add: lazyList.clear(); kept.forEach(lazyList::add).","Move the retainAll logic upstream so the LazyPropertyList only ever receives its final contents.","If intersection semantics are essential, maintain a plain List<T> alongside and sync on build."],"exampleFix":"// before\nlazyList.retainAll(allowed); // UnsupportedOperationException\n// after\nList<T> kept = lazyList.stream().filter(allowed::contains).toList();\nlazyList.clear();\nkept.forEach(lazyList::add);","handlingStrategy":"validation","validationCode":"if (list instanceof LazyPropertyList) {\n    throw new IllegalStateException(\"retainAll() not supported on LazyPropertyList\");\n}","typeGuard":"static boolean isLazyPropertyList(List<?> l) { return l instanceof LazyPropertyList; }","tryCatchPattern":"try { list.retainAll(c); } catch (UnsupportedOperationException e) { /* rebuild: intersect and clear+addAll */ }","preventionTips":["Compute retained entries into a new list, clear, then re-add.","Move intersection logic upstream of the LazyPropertyList.","Prefer filtering at the provider/ListProperty level for Gradle tasks."],"tags":["gradle","build-tools","lazy-collection","unsupported-operation"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}