apache/druid · error · IllegalStateException
written out already
Error message
written out already
What it means
BlockLayoutColumnarDoublesSerializer.add(double) appends a double to the block-based serializer. Once the serializer is closed (endBuffer nulled out by close/flush), it throws IllegalStateException('written out already') because writing after finalization would corrupt the segment file.
Source
Thrown at processing/src/main/java/org/apache/druid/segment/data/BlockLayoutColumnarDoublesSerializer.java:93
}
@Override
public void open() throws IOException
{
flattener.open();
}
@Override
public int size()
{
return numInserted;
}
@Override
public void add(double value) throws IOException
{
if (endBuffer == null) {
throw new IllegalStateException("written out already");
}
if (!endBuffer.hasRemaining()) {
endBuffer.rewind();
flattener.write(endBuffer);
endBuffer.clear();
}
endBuffer.putDouble(value);
++numInserted;
if (numInserted < 0) {
throw new ColumnCapacityExceededException(columnName);
}
}
@Override
public long getSerializedSize() throws IOException
{
writeEndBuffer();View on GitHub (pinned to 9b90983fd2)
Solutions
- Ensure no add() calls occur after close()/write-out; finish all adds first
- Create a new serializer instance for each column/write session
- Check serializer state (endBuffer != null) before adding in defensive code
Example fix
// before serializer.close(); serializer.add(value); // after serializer.add(value); serializer.close();
Defensive patterns
Strategy: try-catch
Validate before calling
if (serializer != null && serializer.isOpen()) { serializer.add(value); } Type guard
boolean canAdd(BlockLayoutColumnarDoublesSerializer s) { return s != null && s.isOpen(); } // if state accessor exists; otherwise track closed flag locally Try / catch
try { serializer.add(value); } catch (IllegalStateException e) { throw new IOException("Serializer already closed; row count exceeded declared numRows", e); } Prevention
- Write all values before calling close(); never add after finalization
- Track writer lifecycle with explicit open/closed state
- Create a fresh serializer per column; do not reuse closed instances
When it happens
Trigger: Calling add() after close() (or after the serializer finished writing its last block and was written out), e.g. continuing to feed values after completing a column.
Common situations: Ingestion/task code paths that write more rows than declared; exception-handling bugs that retry add after close; reusing a serializer instance for a second column.
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
- Couldn't deserialize authenticator userMap!
- Couldn't serialize authenticator userMap!
- Couldn't deserialize authorizer userMap!
- Couldn't serialize authorizer userMap!
- Couldn't deserialize authorizer groupMappingMap!
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/739e2df053e402f0.
Report an issue: GitHub.