{"record":{"id":"3283922aaff6184b","repo":"microsoft/FASTER","slug":"out-of-order-message-within-session","errorCode":null,"errorMessage":"Out of order message within session","messagePattern":"Out of order message within session","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"cs/remote/src/FASTER.client/ClientSession.cs","lineNumber":347,"sourceCode":"            Dispose();\n        }\n\n\n        int lastSeqNo = -1;\n        readonly Dictionary<int, (Key, Value, Context)> pubsubPendingContext = new();\n        readonly Dictionary<int, (Key, Input, Output, Context)> readRmwPendingContext = new();\n        readonly Dictionary<int, TaskCompletionSource<(Status, Output)>> readRmwPendingTcs = new();\n\n        internal void ProcessReplies(byte[] buf, int offset)\n        {\n            Output defaultOutput = default;\n            fixed (byte* b = &buf[offset])\n            {\n                var src = b;\n                var seqNo = ((BatchHeader*)src)->SeqNo;\n                var count = ((BatchHeader*)src)->NumMessages;\n                if (seqNo != lastSeqNo + 1 && !subscriptionSession)\n                    throw new Exception(\"Out of order message within session\");\n                lastSeqNo = seqNo;\n\n                src += BatchHeader.Size;\n\n                for (int i = 0; i < count; i++)\n                {\n                    switch ((MessageType)(*src++))\n                    {\n                        case MessageType.Upsert:\n                            {\n                                var status = ReadStatus(ref src);\n                                (Key, Value, Context) result = upsertQueue.Dequeue();\n                                functions.UpsertCompletionCallback(ref result.Item1, ref result.Item2, result.Item3);\n                                break;\n                            }\n                        case MessageType.UpsertAsync:\n                            {\n                                var status = ReadStatus(ref src);","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/remote/src/FASTER.client/ClientSession.cs#L329-L365","documentation":"FASTER's client session processes reply batches from the server in strict sequence-number order. Each incoming batch carries a BatchHeader whose SeqNo must equal lastSeqNo + 1; if it does not, the client's reply stream is corrupted or messages were dropped/reordered by the network or server, so the library throws to prevent silently applying out-of-order responses. Subscription sessions are exempt because their delivery order is inherently asynchronous.","triggerScenarios":"ProcessReplies receives a batch whose BatchHeader.SeqNo is not lastSeqNo + 1 on a non-subscription session — typically after a dropped TCP packet boundary mismatch, a server restart mid-stream, or concurrent use of the same ClientSession from multiple threads interleaving reads.","commonSituations":"Sharing a single ClientSESSION across threads without synchronization; reconnecting a socket while the server still holds a different sequence state; a custom network proxy/relay reordering frames; running against a modified server build that resets or skips sequence numbers.","solutions":["Ensure one ClientSession instance is used by exactly one thread (or serialized externally) at a time; replies for a session are not safe to process concurrently.","Recreate the session (and reconnect) after any network disruption so sequence numbers on both ends restart in sync.","Check the server deployment for restarts or proxies that can reorder/drop TCP frames; FASTER assumes reliable in-order TCP delivery.","If you maintain a fork, verify the server increments SeqNo per message exactly as the client's BatchHeader expects."],"exampleFix":"// before: shared session across threads\nclientSession.Read(...); // thread A\nclientSession.Read(...); // thread B\n\n// after: one session per logical consumer, or lock around use\nlock (sessionLock) { clientSession.Read(...); }","handlingStrategy":"try-catch","validationCode":"// verify single-threaded use and healthy connection before reading\nif (sessionDisposed || !socketConnected) { reconnectAndRecreateSession(); }","typeGuard":null,"tryCatchPattern":"try { session.ProcessRepliesAndWait(...); }\ncatch (Exception ex) when (ex.Message == \"Out of order message within session\")\n{\n    // sequence stream corrupted: discard session, reconnect, rebuild session\n    session.Dispose(); session = CreateNewSession();\n}","preventionTips":["Never share a ClientSession across threads without locking.","Recreate the session after any reconnect or server restart.","Avoid proxies/middleboxes that can reorder TCP frames on the FASTER channel.","Keep client and server on matching FASTER versions."],"tags":["csharp","networking","sequencing","faster"],"backgroundTag":"unexpected-response-shape","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}