apache/cassandra · error · IllegalStateException

unknown stream session

Error message

unknown stream session: %s - %d

What it means

When deserializing an incoming IncomingStreamMessage, the receiver looks up the stream session identified by the header's planId/sessionIndex via StreamManager.instance.findSession. If no session matches, it throws IllegalStateException because streaming data arrived for a session this node does not know about; the incoming bytes cannot be attributed to any live transfer.

Solutions

  1. Re-initiate streaming from the sender so a new session/planId is negotiated (e.g. re-run repair or nodetool rebuild)
  2. Check both nodes' logs for the earlier session failure/timeout that removed the session and fix that root cause (timeouts, dropped sessions, GC pauses)
  3. Ensure cluster clocks and stream_manager settings are consistent; verify no node restart occurred mid-stream
  4. If streaming persistently fails, run nodetool cleanup/repair after fixing connectivity and retry the operation

Example fix

// after 'unknown stream session' during repair
nodetool repair -pr <keyspace> <table>   // restart the operation; a fresh session/planId is created
Defensive patterns

Strategy: retry

Try / catch

try { deliverStream(header, input); }
catch (IllegalStateException e) {
    if (e.getMessage().startsWith("unknown stream session")) {
        logger.warn("stale stream data for {}, asking peer to resend", header.planId);
        requestStreamRestart(header.planId);
    } else throw e;
}

Prevention

When it happens

Trigger: A peer sends stream data referencing a planId/sessionIndex that was already finished, failed, cancelled, or never registered on this node — e.g. message arrives after the local session timed out or was aborted, or the node restarted mid-stream.

Common situations: Long-running streaming exceeding timeouts so the receiver's session is dropped while sender keeps sending; node restart during streaming; orphaned sender retries after a failed stream session; mismatched cluster state after bootstrap/decommission interruption.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/2bb5e62c0db059c2. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/streaming/messages/IncomingStreamMessage.java:41

import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.streaming.IncomingStream;
import org.apache.cassandra.streaming.StreamManager;
import org.apache.cassandra.streaming.StreamReceiveException;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.streaming.StreamingChannel;
import org.apache.cassandra.streaming.StreamingDataOutputPlus;

public class IncomingStreamMessage extends StreamMessage
{
    public static Serializer<IncomingStreamMessage> serializer = new Serializer<IncomingStreamMessage>()
    {
        public IncomingStreamMessage deserialize(DataInputPlus input, int version) throws IOException
        {
            StreamMessageHeader header = StreamMessageHeader.serializer.deserialize(input, version);
            StreamSession session = StreamManager.instance.findSession(header.sender, header.planId, header.sessionIndex, header.sendByFollower);
            if (session == null)
                throw new IllegalStateException(String.format("unknown stream session: %s - %d", header.planId, header.sessionIndex));
            ColumnFamilyStore cfs = ColumnFamilyStore.getIfExists(header.tableId);
            if (cfs == null)
                throw new StreamReceiveException(session, "CF " + header.tableId + " was dropped during streaming");

            try
            {
                IncomingStream incomingData = cfs.getStreamManager().prepareIncomingStream(session, header);
                incomingData.read(input, version);

                return new IncomingStreamMessage(incomingData, header);
            }
            catch (Throwable t)
            {
                if (t instanceof StreamReceiveException)
                    throw (StreamReceiveException) t;
                // make sure to wrap so the caller always has access to the session to call onError
                throw new StreamReceiveException(session, t);
            }

View on GitHub (pinned to 88fd0f6a0e)