{"record":{"id":"ad6c5e5f56be2772","repo":"eclipse-vertx/vert.x","slug":"read-only-ad6c5e","errorCode":null,"errorMessage":"Read only","messagePattern":"Read only","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/http/impl/headers/Http1xHeaders.java","lineNumber":403,"sourceCode":"            this.index = index;\n            this.entry = entry;\n            this.expectedModCount = expectedModCount;\n          }\n\n          @Override\n          public String getKey() {\n            return entry.key.toString();\n          }\n\n          @Override\n          public String getValue() {\n            return entry.value.toString();\n          }\n\n          @Override\n          public String setValue(String value) {\n            if (readOnly) {\n              throw new IllegalStateException(\"Read only\");\n            }\n            if (expectedModCount != modCount) {\n              throw new ConcurrentModificationException();\n            }\n            if (ref != null) {\n              copyOnWrite();\n              int i = 0;\n              MapEntry e = head;\n              for (;i < index; i++) {\n                e = e.after;\n              }\n              entry = e;\n              MapEntry c = null;\n              while (++i < numberOfIterations) {\n                e = e.after;\n                c = e;\n              }\n              int modCountValue = ++modCount;","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/http/impl/headers/Http1xHeaders.java#L385-L421","documentation":"Http1xHeaders is an HTTP/1.x headers implementation backed by a copy-on-write structure. MapEntry.setValue(String) throws IllegalStateException(\"Read only\") when the headers are flagged readOnly, and also throws ConcurrentModificationException if the iterator's expectedModCount is stale.","triggerScenarios":"Mutating an entry via entry.setValue(...) obtained from an iterator over an Http1xHeaders instance that has been marked read-only (e.g. frozen after the request/response completed or shared as immutable).","commonSituations":"Rewriting header values while iterating over finished-response headers; in-place header rewriting after the exchange completed; reusing iterators across lifecycle phases.","solutions":["Create a mutable copy of the headers and edit entries there","Perform entry mutations before the HTTP message is finalized","Recreate the headers object for the next lifecycle instead of mutating the frozen one","Check readOnly (or catch IllegalStateException) before mutating through the iterator"],"exampleFix":"// before\nfor (Map.Entry<String,String> e : headers) { e.setValue(lower(e.getValue())); } // may be read-only\n// after\nMultiMap copy = MultiMap.caseInsensitiveMultiMap().addAll(headers);\nfor (Map.Entry<String,String> e : copy.entries()) { e.setValue(lower(e.getValue())); }","handlingStrategy":"try-catch","validationCode":"if (headers instanceof Http1xHeaders h && h.size() > 0 && isFrozenLifecycle()) { /* do not mutate via entry.setValue */ }","typeGuard":null,"tryCatchPattern":"try { entry.setValue(newValue); } catch (IllegalStateException e) { /* read-only: rebuild headers */ MultiMap copy = MultiMap.caseInsensitiveMultiMap().addAll(headers); copy.set(entry.getKey(), newValue); } catch (ConcurrentModificationException e) { /* re-acquire iterator */ }","preventionTips":["Do not mutate entries through iterators on completed exchanges","Re-acquire iterators after any header mutation to avoid ConcurrentModificationException","Copy headers before transforming entry values"],"tags":["http","headers","http1x","immutable","iterator"],"backgroundTag":"invalid-state-transition","analyzedSha":"fb308bd8c3f12c79f4ae89bef67fadf6c80d036e","analyzedAt":"2026-09-06T11:37:12.241Z","contentChangedAt":"2026-09-06T11:37:12.241Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}