{"record":{"id":"19188f1f55501998","repo":"apache/druid","slug":"written-out-already-19188f","errorCode":null,"errorMessage":"written out already","messagePattern":"written out already","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/data/CompressedBlockSerializer.java","lineNumber":85,"sourceCode":"  )\n  {\n    this.segmentWriteOutMedium = segmentWriteOutMedium;\n    this.compression = compression;\n    this.compressor = compression.getCompressor();\n    this.uncompressedDataBuffer = compressor.allocateInBuffer(blockSize, closer).order(ByteOrder.nativeOrder());\n    this.compressedDataBuffer = compressor.allocateOutBuffer(blockSize, closer).order(ByteOrder.nativeOrder());\n  }\n\n  public void open() throws IOException\n  {\n    headerOut = segmentWriteOutMedium.makeWriteOutBytes();\n    valuesOut = segmentWriteOutMedium.makeWriteOutBytes();\n  }\n\n  public void addValue(byte[] bytes) throws IOException\n  {\n    if (uncompressedDataBuffer == null) {\n      throw new IllegalStateException(\"written out already\");\n    }\n    flushIfNeeded();\n\n    if (bytes.length <= uncompressedDataBuffer.remaining()) {\n      uncompressedDataBuffer.put(bytes);\n    } else {\n      int written = 0;\n      // write until we have had our fill, flushing buffers as needed\n      while (written < bytes.length) {\n        int next = Math.min(uncompressedDataBuffer.remaining(), bytes.length - written);\n        uncompressedDataBuffer.put(bytes, written, next);\n        written += next;\n        flushIfNeeded();\n      }\n    }\n  }\n\n  public void addValue(ByteBuffer bytes) throws IOException","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/data/CompressedBlockSerializer.java#L67-L103","documentation":"CompressedBlockSerializer.addValue(byte[]) throws IllegalStateException(\"written out already\") when uncompressedDataBuffer is null, i.e., the serializer has already been finished (close()/writeEndBuffer ran) and its buffers were released. No further values can be accepted after write-out.","triggerScenarios":"Calling addValue(byte[]) after close() or after the end buffer was written during segment serialization.","commonSituations":"Custom dictionary/value encoders that keep adding values after the column is finalized; plugin code with wrong lifecycle ordering between value collection and column close; reusing a closed serializer for another batch.","solutions":["Complete all addValue calls before calling close/writeEndBuffer","Fix ordering in custom encoders so value writing precedes finalization","Do not reuse serializer instances after close; create a new one per column","Add an assertion/flag in wrapping code to catch late adds"],"exampleFix":"// before\nserializer.addValue(bytes); // after close()\n// after\nif (serializer.isOpen()) {\n  serializer.addValue(bytes);\n}","handlingStrategy":"validation","validationCode":"if (uncompressedDataBuffer == null || closed) {\n  throw new IllegalStateException(\"CompressedBlockSerializer already written out\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  serializer.addValue(bytes);\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"written out already\")) {\n    // reopen a new serializer for remaining values\n    serializer = createSerializer();\n    serializer.addValue(bytes);\n  } else throw e;\n}","preventionTips":["Call close() only after all values are added","Track open/closed state in wrapping code","Avoid double-close paths in plugins","Unit-test encoder lifecycle ordering"],"tags":["druid","segment","illegal-state","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}