eclipse-vertx/vert.x · error · IllegalStateException
Read only
Error message
Read only
What it means
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.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/headers/Http1xHeaders.java:403
this.index = index;
this.entry = entry;
this.expectedModCount = expectedModCount;
}
@Override
public String getKey() {
return entry.key.toString();
}
@Override
public String getValue() {
return entry.value.toString();
}
@Override
public String setValue(String value) {
if (readOnly) {
throw new IllegalStateException("Read only");
}
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
if (ref != null) {
copyOnWrite();
int i = 0;
MapEntry e = head;
for (;i < index; i++) {
e = e.after;
}
entry = e;
MapEntry c = null;
while (++i < numberOfIterations) {
e = e.after;
c = e;
}
int modCountValue = ++modCount;View on GitHub (pinned to fb308bd8c3)
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
Example fix
// before
for (Map.Entry<String,String> e : headers) { e.setValue(lower(e.getValue())); } // may be read-only
// after
MultiMap copy = MultiMap.caseInsensitiveMultiMap().addAll(headers);
for (Map.Entry<String,String> e : copy.entries()) { e.setValue(lower(e.getValue())); } Defensive patterns
Strategy: try-catch
Validate before calling
if (headers instanceof Http1xHeaders h && h.size() > 0 && isFrozenLifecycle()) { /* do not mutate via entry.setValue */ } Try / catch
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 */ } Prevention
- 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
When it happens
Trigger: 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).
Common situations: Rewriting header values while iterating over finished-response headers; in-place header rewriting after the exchange completed; reusing iterators across lifecycle phases.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- a header value contains a prohibited character '127': <value
- a header value must not end with '\r' or '\n':<seq>
- a header value contains a prohibited character '127': <seq>
- only '\n' is allowed after '\r': <seq>
- only ' ' and '\t' are allowed after '\n': <seq>
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/ad6c5e5f56be2772.
Report an issue: GitHub.