{"record":{"id":"45a99a88d366dfea","repo":"eclipse-vertx/vert.x","slug":"read-only-45a99a","errorCode":null,"errorMessage":"Read only","messagePattern":"Read only","errorType":"exception","errorClass":"java.lang.IllegalStateException","httpStatus":null,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/http/impl/headers/HttpHeaders.java","lineNumber":123,"sourceCode":"\n  @Override\n  public boolean isEmpty() {\n    return headers.isEmpty();\n  }\n\n  @Override\n  public Set<String> names() {\n    Set<String> names = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);\n    for (Map.Entry<CharSequence, CharSequence> header : headers) {\n      names.add(header.getKey().toString());\n    }\n    return names;\n  }\n\n  @Override\n  public HttpHeaders add(String name, String value) {\n    if (!mutable) {\n      throw new IllegalStateException(\"Read only\");\n    }\n    if (!HttpHeadersInternal.DISABLE_HTTP_HEADERS_VALIDATION) {\n      HttpUtils.validateHeader(name, value);\n    }\n    headers.add(HttpUtils.toLowerCase(name), value);\n    return this;\n  }\n\n  @Override\n  public HttpHeaders add(String name, Iterable<String> values) {\n    if (!mutable) {\n      throw new IllegalStateException(\"Read only\");\n    }\n    if (!HttpHeadersInternal.DISABLE_HTTP_HEADERS_VALIDATION) {\n      HttpUtils.validateHeader(name, values);\n    }\n    headers.add(HttpUtils.toLowerCase(name), values);\n    return this;","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/http/impl/headers/HttpHeaders.java#L105-L141","documentation":"The default HttpHeaders implementation (io.vertx.core.http.impl.headers.HttpHeaders) guards every mutating method with a mutable flag. add(String, String) throws IllegalStateException(\"Read only\") when the instance is read-only, e.g. when you hold headers obtained from a completed response or an immutable shared constant. The object is fine for reading; mutation requires a mutable instance.","triggerScenarios":"Calling add/addAll on a read-only HttpHeaders — commonly response.headers() after the response completed, or HttpHeaders objects shared as base headers (setBaseHeaders) that were created immutable.","commonSituations":"Reusing one HttpHeaders template for many requests and calling addAll on it (addAll mutates the target, not the source); copying response headers into a new request by mutating the response's headers object directly.","solutions":["Build the new headers set separately: create a mutable HttpHeaders and copy the read-only entries into it (e.g. request.headers().setAll(readOnlyHeaders)).","For per-request customization, start from request.headers() (mutable) and add base headers into it, never mutate the base template.","Check the mutability contract before mutating; in Vert.x use a fresh HttpHeaders created via the factory instead of reusing response headers.","If you own the code that produced the read-only headers, create them mutable (or wrap with a mutable copy) at construction time."],"exampleFix":"// before\nresponse.headers().add(\"X-Extra\", \"v\"); // Read only\n// after\nHttpHeaders extra = HttpHeaders.setAll(response.headers());\nextra.add(\"X-Extra\", \"v\");","handlingStrategy":"validation","validationCode":"// Track mutability at the call site: only call add on headers you created\nHttpHeaders target = HttpHeaders.setAll(response.headers()); // copy first\ntarget.add(\"X-Extra\", \"v\");","typeGuard":null,"tryCatchPattern":"try { headers.add(name, value); } catch (IllegalStateException e) { HttpHeaders copy = HttpHeaders.setAll(headers); copy.add(name, value); }","preventionTips":["Treat headers() from a completed exchange as immutable; copy before editing.","Customize request.headers() before sending instead of editing response headers.","Keep immutable base-header templates separate from per-request mutable copies.","Pass mutable HttpHeaders explicitly when helpers need to mutate."],"tags":["http","headers","immutable-state"],"backgroundTag":"headers-already-sent","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"}