Why STOMP Receipts and Errors Don’t Play Well on One Connection

STOMP is layered on top of TCP, which is a byte stream, not message-oriented. When you use a single TCP connection for both sending client frames and receiving server frames, two problems arise:

  1. Interleaving ambiguity
    The client cannot strictly associate an incoming ERROR or RECEIPT frame with the exact outbound frame that triggered it, because inbound frames arrive asynchronously and may be intermixed with unrelated messages (e.g. a MESSAGE from a subscription). TCP guarantees order of bytes, not logical pairing of request–response.
  2. State confusion under error conditions
    If the server sends an ERROR frame, it may apply to a specific client command, to a subscription, or to the connection as a whole. Over a shared connection, the client may still have pending sends “in flight” when the error is received, making it unclear which ones succeeded, failed, or need to be retried.

In short: TCP multiplexes all traffic into one ordered stream, but STOMP requires the client to distinguish between different categories of frames (responses, errors, and unrelated deliveries). This mismatch makes a single-connection setup prone to race conditions and mis-association of responses.

👉 That’s why some designs prefer separate connections for send-only vs. receive-only paths (or use correlation IDs carefully), to reduce ambiguity.

Below is a timeline diagram to make the interleaving problem visually clear:

Here’s the timeline:

  • The client sends two frames (SEND id=1, then SEND id=2).
  • The server interleaves unrelated MESSAGE frames with the RECEIPT for id=1.
  • An ERROR arrives, but it’s unclear whether it corresponds to SEND id=2 or some other condition.

This illustrates why a single TCP connection makes associating responses with requests error-prone — asynchronous events from subscriptions are mixed with acknowledgments and errors.