{"record":{"id":"ed8f5c947a23307d","repo":"eclipse-vertx/vert.x","slug":"only-the-context-thread-can-write-a-message","errorCode":null,"errorMessage":"Only the context thread can write a message","messagePattern":"Only the context thread can write a message","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/http/impl/http1/Http1ServerResponse.java","lineNumber":107,"sourceCode":"                      Http1ServerConnection conn,\n                      HttpRequest request,\n                      Object requestMetric,\n                      boolean keepAlive) {\n    this.vertx = vertx;\n    this.conn = conn;\n    this.context = context;\n    this.version = request.protocolVersion();\n    this.headers = Http1xHeaders.httpHeaders();\n    this.request = request;\n    this.status = HttpResponseStatus.OK;\n    this.requestMetric = requestMetric;\n    this.keepAlive = keepAlive;\n    this.head = request.method() == io.netty.handler.codec.http.HttpMethod.HEAD;\n  }\n\n  private void checkThread() {\n    if (conn.strictThreadMode && !context.executor().inThread()) {\n      throw new IllegalStateException(\"Only the context thread can write a message\");\n    }\n  }\n\n  @Override\n  public MultiMap headers() {\n    return headers;\n  }\n\n  @Override\n  public MultiMap trailers() {\n    if (trailers == null) {\n      Http1xHeaders v = Http1xHeaders.httpHeaders();\n      trailers = v;\n      trailingHeaders = v;\n    }\n    return trailers;\n  }\n","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/http/impl/http1/Http1ServerResponse.java#L89-L125","documentation":"Thrown by Http1ServerResponse.checkThread() when conn.strictThreadMode is enabled and a write/end call is made from a thread other than the request's context (event-loop) thread. Vert.x strict thread mode enforces that all response writes happen on the owning context to keep single-threaded guarantees.","triggerScenarios":"Calling response.write(), response.end(), writeHead(), writeContinue(), writeEarlyHints() or netSocket() from a worker thread, a custom executor, or a user thread while the connection was created with strictThreadMode=true.","commonSituations":"Writing the response from a CompletableFuture callback or application thread pool; blocking worker doing the response; tests running code on the main thread.","solutions":["Dispatch the write onto the request's context with context.runOnContext(v -> response.end()) or vertx.runOnContext","Perform writes only inside Vert.x handlers (event loop), keep other threads for computation only","Disable strictThreadMode if cross-thread writes are intentional (not recommended)"],"exampleFix":"// before\nexecutor.submit(() -> response.end(\"done\"));\n// after\nexecutor.submit(() -> context.runOnContext(v -> response.end(\"done\")));","handlingStrategy":"try-catch","validationCode":"// ensure writes run on the owning context\nContext ctx = vertx.getOrCreateContext();\nif (!ctx.equals(response.vertx().getOrCreateContext())) ctx.runOnContext(v -> response.end());","typeGuard":null,"tryCatchPattern":"try { response.end(buf); } catch (IllegalStateException e) { context.runOnContext(v -> response.end(buf)); }","preventionTips":["Only write responses from Vert.x handler threads","Use context.runOnContext when jumping threads","Avoid ending responses from custom executor pools","Keep strictThreadMode in mind when designing worker usage"],"tags":["http","threading","event-loop","http1"],"backgroundTag":"unsupported-operation","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"}