{"record":{"id":"3813c92df209b237","repo":"eclipse-vertx/vert.x","slug":"head-already-written","errorCode":null,"errorMessage":"Head already written","messagePattern":"Head already written","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/http/impl/http1/Http1ServerResponse.java","lineNumber":510,"sourceCode":"  private Future<Void> sendFileInternal(long offset, long length, long size, RandomAccessFile file, FileChannel fileChannel, boolean close) {\n    Future<Void> ret = null;\n    try {\n      ContextInternal ctx = vertx.getOrCreateContext();\n      if (offset < 0) {\n        return ctx.failedFuture(\"offset : \" + offset + \" (expected: >= 0)\");\n      }\n      if (length < 0) {\n        return ctx.failedFuture(\"length : \" + length + \" (expected: >= 0)\");\n      }\n      long actualLength = Math.min(length, size - offset);\n      long actualOffset = Math.min(offset, size);\n      if (actualLength < 0) {\n        return ctx.failedFuture(\"offset : \" + offset + \" is larger than the requested file length : \" + size);\n      }\n      synchronized (conn) {\n        checkValid();\n        if (headWritten) {\n          throw new IllegalStateException(\"Head already written\");\n        }\n\n        // fail early before status code/headers are written to the response\n        prepareHeaders(actualLength);\n        bytesWritten = actualLength;\n        written = true;\n        conn.write(new VertxAssembledHttpResponse(head, version, status, headers), null);\n        FileChannel toSend = fileChannel == null ? file.getChannel() : fileChannel;\n        ChannelFuture channelFuture = conn.sendFile(toSend, actualOffset, actualLength);\n        PromiseInternal<Void> promise = context.promise();\n        ret = promise.future();\n        channelFuture.addListener(future -> {\n          if (future.isSuccess()) {\n\n            // signal body end handler\n            Handler<Void> handler;\n            synchronized (conn) {\n              handler = bodyEndHandler;","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/http/impl/http1/Http1ServerResponse.java#L492-L528","documentation":"Thrown by Http1ServerResponse.sendFileInternal (reached via sendFile) when the response head has already been written. sendFile must write status code and headers itself (including Content-Length), so it cannot run once the head is out.","triggerScenarios":"Calling response.sendFile(path) after headers were flushed — e.g. after writeHead(), after a previous partial sendFile attempt, or after end()/write() implicitly wrote the head.","commonSituations":"Conditional logic where one branch writes the body/headers and another calls sendFile; retrying a failed sendFile after headers were prepared; sendFile called from both middleware and handler.","solutions":["Call sendFile before any other write/end on the response","Remove preceding writeHead()/write() calls when sendFile will be used","Return early from the handler once sendFile is invoked"],"exampleFix":"// before\nresponse.putHeader(\"Content-Type\", \"text/plain\");\nresponse.write(\"hi\");\nresponse.sendFile(\"file.txt\");\n// after\nresponse.putHeader(\"Content-Type\", \"text/plain\");\nresponse.sendFile(\"file.txt\");","handlingStrategy":"validation","validationCode":"if (!response.headWritten()) {\n  response.sendFile(\"/path/file\");\n}","typeGuard":null,"tryCatchPattern":"try { response.sendFile(path); } catch (IllegalStateException e) { /* head already written; cannot sendFile now */ }","preventionTips":["Always sendFile before any write/end","Do not mix sendFile with manual body writes","Handle sendFile errors without retrying after header flush"],"tags":["http","response","sendfile","http1"],"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-14T05:17:10.506Z"}