{"record":{"id":"728ee7a5a9f69eee","repo":"jhy/jsoup","slug":"use-iterator-remove-instead-to-remove-attributes","errorCode":null,"errorMessage":"Use Iterator#remove() instead to remove attributes while iterating.","messagePattern":"Use Iterator#remove\\(\\) instead to remove attributes while iterating\\.","errorType":"exception","errorClass":"ConcurrentModificationException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/jsoup/nodes/Attributes.java","lineNumber":502,"sourceCode":"                        break;\n                }\n\n                return i < size;\n            }\n\n            @Override\n            public Attribute next() {\n                checkModified();\n                if (i >= size) throw new NoSuchElementException();\n                String key = keys[i];\n                assert key != null;\n                final Attribute attr = new Attribute(key, (String) vals[i], Attributes.this);\n                i++;\n                return attr;\n            }\n\n            private void checkModified() {\n                if (size != expectedSize) throw new ConcurrentModificationException(\"Use Iterator#remove() instead to remove attributes while iterating.\");\n            }\n\n            @Override\n            public void remove() {\n                Attributes.this.remove(--i); // next() advanced, so rewind\n                expectedSize--;\n            }\n        };\n    }\n\n    /**\n     Get the attributes as a List, for iteration.\n     @return a view of the attributes as an unmodifiable List.\n     */\n    public List<Attribute> asList() {\n        ArrayList<Attribute> list = new ArrayList<>(size);\n        for (int i = 0; i < size; i++) {\n            String key = keys[i];","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/jhy/jsoup/blob/9851ac5d9c576c6888910b5a51a2362bbc978959/src/main/java/org/jsoup/nodes/Attributes.java#L484-L520","documentation":"jsoup's Attributes iterator is fail-fast: it captures the attribute count when created and throws ConcurrentModificationException if the map's size changes during iteration by any means other than the iterator's own remove() method. This prevents undefined behavior while iterating the internal arrays.","triggerScenarios":"Calling Attributes.remove(key), size-changing operations, or modifying attributes on the owning Element while looping over attributes() iterator or an enhanced for loop, instead of using iterator.remove().","commonSituations":"Removing attributes inside a for (Attribute a : element.attributes()) loop; clearing attributes while streaming over them; concurrent code mutating an element's attributes during traversal.","solutions":["Use the iterator's remove() method to delete the current attribute instead of attributes.remove(key) inside the loop","Collect keys to remove into a temporary list first, then remove them after iteration completes","Iterate over a snapshot copy, e.g. new ArrayList<>(attributes.asList()), if mutation during the loop is unavoidable","Synchronize or restructure concurrent access so attributes are not modified while another thread iterates"],"exampleFix":"// before\nfor (Attribute a : node.attributes()) {\n    if (a.getKey().startsWith(\"data-\")) node.attributes().remove(a.getKey()); // CME\n}\n// after\nIterator<Attribute> it = node.attributes().iterator();\nwhile (it.hasNext()) {\n    if (it.next().getKey().startsWith(\"data-\")) it.remove();\n}","handlingStrategy":"type-guard","validationCode":"// remove after iteration\nList<String> toRemove = new ArrayList<>();\nfor (Attribute a : node.attributes()) if (a.getKey().startsWith(\"tmp-\")) toRemove.add(a.getKey());\ntoRemove.forEach(k -> node.attributes().remove(k));","typeGuard":"// iterate a snapshot copy instead of the live map\nList<Attribute> snapshot = new ArrayList<>(node.attributes().asList());","tryCatchPattern":"try { for (Attribute a : node.attributes()) { ... } } catch (ConcurrentModificationException e) { /* switch to iterator.remove() or snapshot iteration */ }","preventionTips":["Use iterator.remove() for removal during iteration","Snapshot attributes (asList copy) when mutation is possible","Avoid mutating shared nodes from multiple threads","Keep removal logic outside of traversal loops where possible"],"tags":["concurrent-modification","iterator","collections"],"backgroundTag":"invalid-state-transition","analyzedSha":"9851ac5d9c576c6888910b5a51a2362bbc978959","analyzedAt":"2026-09-08T15:22:04.931Z","contentChangedAt":"2026-09-08T15:22:04.931Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}