apache/cassandra · critical · ProtocolException
Attempted to encode a response with an unset stream id:
Error message
Attempted to encode a response with an unset stream id:
What it means
Fatal ProtocolException thrown when the native-protocol layer tries to encode a server Response whose streamId is still UNSET_STREAM_ID. A response must carry the stream id of the request it answers; an unset id means a server code path produced a response without routing information, and sending it could deliver it to an unrelated in-flight request (CASSANDRA-21508). The connection is torn down intentionally to avoid mis-routing.
Solutions
- Find the server code path that created the Response and ensure streamId is set from the originating request before encoding (response.setStreamId(request.streamId)).
- Check server logs for the stack trace preceding the connection teardown to identify the producing handler.
- Upgrade to a version containing the fix for the specific handler bug (search JIRA for CASSANDRA-21508 follow-ups).
- As a client-side mitigation, expect the connection to close and rely on driver reconnection/retry; the fatal exception is intentional protection.
- If seen without any local patches, report with full server logs — it indicates an internal invariant violation.
Example fix
// before Message.Response resp = Message.response(type, message); channel.writeAndFlush(resp); // stream id never set // after resp.setStreamId(message.getStreamId()); channel.writeAndFlush(resp);
Defensive patterns
Strategy: type-guard
Validate before calling
// server-side guard before writing a response
if (response.getStreamId() == Message.UNSET_STREAM_ID)
throw new IllegalStateException("Response written without stream id: " + response); Type guard
boolean hasRoutedStreamId(Message.Response r) { return r.getStreamId() != Message.UNSET_STREAM_ID; } Prevention
- Always derive responses from the request object so setStreamId(request.streamId) is not forgotten.
- Add assertions/tests covering every error path that writes a Response.
- On the client side, treat abrupt connection closes after an error as server-side routing failure and reconnect/retry idempotent requests.
When it happens
Trigger: A server handler path creates a Response (e.g. an ERROR response or event) and writes it without calling setStreamId with the originating request's stream id — typically in custom/modified request handlers, early-failure paths that build responses before pairing with a request, or internal code paths responding outside the normal request dispatch.
Common situations: Running patched/unreleased Cassandra builds where a new error path forgets to stamp the stream id; a bug in driver-facing request handling causing connection resets; after CASSANDRA-21508, responses that previously would have been silently mis-routed now kill the connection with this exception in server logs.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Connection Error
- Cannot convert value
- Cannot decode string as UTF8: '" +…
- Cannot serialize RedundantStatus larger than 0xFFFF…
- Codec not found for requested operation
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/d31721aed2adabed.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/transport/Message.java:347
this.warnings = warnings;
return this;
}
public List<String> getWarnings()
{
return warnings;
}
}
public Envelope encode(ProtocolVersion version, int streamId)
{
// A Response's stream id must be stamped before it is serialized to the wire. UNSET_STREAM_ID here
// means a server code path produced a response without routing information; sending it would risk
// delivering it to an unrelated in-flight request (CASSANDRA-21508). Fail fatally so the connection
// is torn down rather than mis-route a response. Checked before the try below so it is not caught and
// re-wrapped (which would carry the unset id forward).
if (streamId == UNSET_STREAM_ID)
throw ProtocolException.toFatalException(new ProtocolException("Attempted to encode a response with an unset stream id: " + this));
int flags = Flag.none();
@SuppressWarnings("unchecked")
Codec<Message> codec = (Codec<Message>)this.type.codec;
try
{
int messageSize = codec.encodedSize(this, version);
ByteBuf body;
if (this instanceof Response)
{
Response message = (Response)this;
TimeUUID tracingId = message.getTracingId();
Map<String, ByteBuffer> customPayload = message.getCustomPayload();
if (tracingId != null)
messageSize += TimeUUID.sizeInBytes();
List<String> warnings = message.getWarnings();
if (warnings != null)
{View on GitHub (pinned to 88fd0f6a0e)