apache/cassandra · error · RuntimeException
Stream receive task of cf already finished.
Error message
Stream receive task %s of cf %s already finished.
What it means
StreamReceiveTask wraps the receiving side of one streaming session for one table. Its getReceiver() hands out the StreamReceiver that incoming file messages are written through. Once the task is done (all files received and the completion runnable ran), the receiver is invalidated, and any late-arriving lookup throws this RuntimeException to prevent use-after-completion of the receiver.
Solutions
- Check StreamReceiveTask task state (done) before calling getReceiver(), or make getReceiver() calls idempotent in the receive path.
- Verify the sender's file/section count matches the receiver's StreamSummary so no extra messages are sent after completion.
- Retry the streaming session (StreamSession failure handling will cancel it) rather than reusing the finished task.
- If reproducible across an upgrade, confirm both nodes run compatible Cassandra streaming versions.
Example fix
// before
StreamReceiver receiver = task.getReceiver(); // may throw if already finished
// after
if (!task.isDone()) {
StreamReceiver receiver = task.getReceiver();
receiver.received(stream);
} else {
logger.warn("Ignoring late stream message for finished task");
} Defensive patterns
Strategy: try-catch
Validate before calling
if (task == null || task.isDone()) { skipOrLog(); } Try / catch
try { StreamReceiver r = task.getReceiver(); r.received(stream); } catch (RuntimeException e) { if (e.getMessage().contains("already finished")) { logger.warn("Late stream message for finished task"); } else { throw e; } } Prevention
- Ensure receiver vs sender file counts match so no extra messages arrive after completion.
- Make completion handlers idempotent and tolerant of duplicate/late messages.
- Keep cluster node versions aligned during upgrades.
When it happens
Trigger: Calling StreamReceiveTask.getReceiver() after task.done has been set, i.e. after all expected incoming streams arrived and OnCompletionRunnable finished the task; typically a duplicate or late IncomingStreamMessage arriving for an already-completed stream task.
Common situations: Network retries delivering a file message twice; sender and receiver disagree on the number of files so an extra message lands after completion; mixing node versions during upgrades where streaming protocol edge cases resend messages.
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
- A node required to move the data consistently is down
- Attempted to release storage-attached index segment builder…
- Bootstrap can be started exactly once, but seems to have…
- Can not start range streaming as all candidates
- Can't join the ring because bootstrap hasn't completed.
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/6a1cb830196150e0.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/streaming/StreamReceiveTask.java:121
done = true;
executor.submit(new OnCompletionRunnable(this));
}
}
public int getTotalNumberOfFiles()
{
return totalStreams;
}
public long getTotalSize()
{
return totalSize;
}
public synchronized StreamReceiver getReceiver()
{
if (done)
throw new RuntimeException(String.format("Stream receive task %s of cf %s already finished.", session.planId(), tableId));
return receiver;
}
private static class OnCompletionRunnable implements Runnable
{
private final StreamReceiveTask task;
public OnCompletionRunnable(StreamReceiveTask task)
{
this.task = task;
}
public void run()
{
try
{
if (ColumnFamilyStore.getIfExists(task.tableId) == null)
{View on GitHub (pinned to 88fd0f6a0e)