apache/cassandra · error · FlushException
This output stream is in an unsafe state after an asynchrono
Error message
This output stream is in an unsafe state after an asynchronous flush failed
What it means
When an asynchronous flush fails for any reason other than a connection reset, propagateFailedFlush throws a FlushException stating the output stream is in an unsafe state after a failed flush. This signals that the stream cannot guarantee what was written, so continued use is unsafe and the stream must be abandoned.
Source
Thrown at src/java/org/apache/cassandra/net/AsyncChannelOutputPlus.java:202
*/
protected void releaseSpace(long bytesFlushed)
{
long newFlushed = flushed + bytesFlushed;
flushed = newFlushed;
Thread thread = waiting;
if (thread != null && signalWhenFlushed <= newFlushed)
LockSupport.unpark(thread);
}
private void propagateFailedFlush() throws IOException
{
Throwable t = flushFailed;
if (t != null)
{
if (SocketFactory.isCausedByConnectionReset(t))
throw new FlushException("The channel this output stream was writing to has been closed", t);
throw new FlushException("This output stream is in an unsafe state after an asynchronous flush failed", t);
}
}
@Override
abstract protected void doFlush(int count) throws IOException;
abstract public long position();
public long flushed()
{
// external flushed (that which has had flush() invoked implicitly or otherwise) == internal flushing
return flushing;
}
public long flushedToNetwork()
{
return flushedToNetwork;
}View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Treat the stream as failed and abort the streaming session; do not reuse the output stream
- Retry the whole transfer on a fresh connection
- Check the cause (FlushException.getCause()) to identify the underlying channel failure
- Review logs for prior close/error events on the channel that left it in a failed state
Example fix
// before
output.waitUntilFlushed(); // FlushException: unsafe state
// then continuing to write
output.write(...);
// after
try {
output.waitUntilFlushed();
} catch (FlushException e) {
output.close();
throw new IOException("Stream failed, restarting transfer", e);
}
Defensive patterns
Strategy: try-catch
Validate before calling
// no pre-call validation exists; rely on failure handling
if (output == null || !output.isOpen()) throw new IOException("Stream not open"); Try / catch
try { output.waitUntilFlushed(); }
catch (FlushException e) {
logger.error("Stream unsafe after flush failure, cause:", e.getCause());
output.close();
restartTransfer();
} Prevention
- Never reuse an AsyncChannelOutputPlus after a failed flush
- Inspect getCause() to distinguish local closes from network errors
- Keep transfer sizes modest so failed transfers are cheap to retry
When it happens
Trigger: waitUntilFlushed completes and finds a stored flushFailed Throwable that is not classified as a connection reset (e.g. closed channel due to local close, SSL errors, generic IO errors on the socket channel).
Common situations: Socket channel closed locally while a flush was pending; TLS handshake/decryption failures mid-stream; disk or OS-level socket errors during internode streaming.
Related errors
- [Stream {}] Error while reading partition {} from stream on
- Checksum didn't match (expected: %d, actual: %d)
- Insufficient disk space to store %s
- FSWriteError (wraps IOException writing sstable component)
- The channel this output stream was writing to has been close
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/710dc65c3a9f79ca.
Report an issue: GitHub.