ZagTrader

FIX Session Health: Heartbeats, Sequence Gaps, Resends and Silences

FIX session health is whether a session stayed connected, in sequence and alive. For each session in the log the analyzer counts Heartbeats against HeartBtInt (108), Test Requests and whether they were answered, sequence gaps in MsgSeqNum (34), resend requests and how they were filled, sequence resets, logons with ResetSeqNumFlag (141), logouts and silences longer than the heartbeat interval.

Free and in your browser: the log is read and analysed on your computer, nothing is uploaded.

What the analyzer shows

Session
One BeginString (8) with one pair of SenderCompID (49) and TargetCompID (56), in either direction. Every figure below is per session.
HeartBtInt
HeartBtInt (108) from the latest Logon (35=A) of the session, in seconds.
Heartbeats
Every Heartbeat (35=0), from both sides.
Test requests
Every Test Request (35=1). A Test Request is unanswered when no Heartbeat from the other side carries the same TestReqID (112) later in the log.
Sequence gaps
MsgSeqNum (34) higher than the next expected number, checked separately for each sender. A Logon with 141=Y or MsgSeqNum 1 restarts the count; a Sequence Reset sets it to NewSeqNo (36).
Sequence too low
MsgSeqNum lower than expected on a message without 43=Y, including a number that was already used.
Resend requests
Every Resend Request (35=2). A resend is incomplete when some numbers from BeginSeqNo (7) to EndSeqNo (16) were neither resent with PossDupFlag (43) Y nor covered by a gap fill later in the log.
Gap fills and sequence resets
Sequence Reset (35=4) with 123=Y is a gap fill; without it, a reset.
Logons and logouts
Every Logon (35=A) and Logout (35=5), with the Logons that carry ResetSeqNumFlag (141) Y counted separately. Logon problems: a Logon not answered, answered by a Logout, or answered with a different HeartBtInt or ResetSeqNumFlag, and messages sent after a Logout without a new Logon.
Silences
Two messages from the same side further apart than HeartBtInt plus 20% (at least 1 second). Not counted after that side has logged out, nor before a Logon.
Longest silence
The longest time between two messages from the same side of the session, not counting the time before a Logon.
Session timeline
Message activity through the log for each session, with markers for logons, logouts, gaps, resends, gap fills, resets, test requests, silences, rejects and CheckSum errors.

Example from the demo log

These figures are calculated from the demo trading day that the analyzer loads with See it on the demo log, so they match what you see there. In the analyzer every bar and row opens the FIX messages behind it.

Session health

One row per session (BeginString and CompID pair)

SessionHeartBtIntHeartbeatsTest req.GapsResendsGap fillsResetsLogons (141=Y)LogoutsSilencesLongest silence
ZAGOMS ↔ BROKERX FIX.4.430 s703011002 (2)2030.0 s
ZAGOMS ↔ ALGOHUB FIX.4.230 s859100002 (2)2145.0 s
ZAGOMS ↔ EUROBRK FIXT.1.130 s960000002 (2)2030.0 s

Session timelines

Messages per 5 min for each session, with session events

  • ZAGOMS ↔ BROKERX

    LogonLogonSession or business rejectSession or business rejectSequence gapResend requestLogoutLogout
  • ZAGOMS ↔ ALGOHUB

    LogonLogonSession or business rejectTest requestSilence over HeartBtIntLogoutLogout
  • ZAGOMS ↔ EUROBRK

    LogonLogonSession or business rejectSession or business rejectCheckSum or BodyLength errorLogoutLogout

Logon and logoutGap, resend, gap fill or resetTest request or silenceReject or CheckSum error

Demo log: one generated trading day (2026-10-19) on three FIX sessions (FIX 4.2, FIX 4.4 and FIXT.1.1).

ZAGOMS ↔ BROKERX (FIX.4.4, HeartBtInt 30 s): 703 Heartbeats, 2 Logons (2 with ResetSeqNumFlag 141=Y) and 2 Logouts; 1 sequence gap, 1 Resend Request answered in full and 2 session Rejects. Longest silence 30.0 s.

ZAGOMS ↔ ALGOHUB (FIX.4.2, HeartBtInt 30 s): 859 Heartbeats, 2 Logons (2 with ResetSeqNumFlag 141=Y) and 2 Logouts; 1 Test Request answered, 1 silence longer than HeartBtInt and 1 session Reject. Longest silence 45.0 s.

ZAGOMS ↔ EUROBRK (FIXT.1.1, HeartBtInt 30 s): 960 Heartbeats, 2 Logons (2 with ResetSeqNumFlag 141=Y) and 2 Logouts; 1 session Reject, 1 Business Message Reject and 1 CheckSum error. Longest silence 30.0 s.

On the sessions without a silence (ZAGOMS ↔ BROKERX and ZAGOMS ↔ EUROBRK), the longest silence is just above HeartBtInt, which is what a healthy session looks like: when nothing else is sent, the Heartbeat is the next message.

How to read it: what good and bad look like

  • On a quiet but healthy session the longest silence sits just above HeartBtInt, because the Heartbeat is the next message. A silence well above HeartBtInt means a Heartbeat was late or missing; the other side should then send a Test Request.
  • An unanswered Test Request is serious: the side that sent it will normally log out or drop the connection soon after. Look at what the other side was doing just before.
  • A sequence gap followed by a Resend Request and resent messages (PossDupFlag 43=Y) or a gap fill is the protocol working as designed. A gap with no resend, or a resend marked incomplete, means messages may have been lost.
  • Gap fills (123=Y) are normal: admin messages are not resent, so they are skipped with a gap fill. A Sequence Reset without GapFillFlag forces the numbers and skips recovery; outside a planned reset it deserves an explanation.
  • Logons with ResetSeqNumFlag (141) Y at the start of the day are a common setup. Several in one day mean the session was reset and reconnected, and any order messages in flight at that moment need checking.
  • More than one Logon and Logout pair per session per day usually means disconnects. Line them up with the silences and gaps on the session timeline.
  • Heartbeats should be roughly one per HeartBtInt per side on a quiet session, and fewer on a busy one, because any message counts as a sign of life.

Common causes and fixes

When a figure on this page looks wrong, these pages explain the errors most often behind it and how to fix them.

For engines: POST a log to the API

The same statistics are available over HTTP for scripts, monitoring jobs and engines. POST a log as text, gzip or a multipart file and get JSON, an HTML report or a plain-text summary back. The request is processed in memory and not stored.

curl --data-binary @fix.log -H "Content-Type: text/plain" "https://fixparser.zagtrader.com/api/v1/analyze"

Fields in the JSON response for this page:

  • stats.sessions[]: one row per session
  • stats.sessions[].heartBtInt, .heartbeats: interval and Heartbeat count
  • stats.sessions[].testRequests, .unansweredTestRequests: test requests
  • stats.sessions[].gaps, .lowSeq: sequence problems
  • stats.sessions[].resendRequests, .incompleteResends, .gapFills, .sequenceResets: recovery
  • stats.sessions[].logons, .resetLogons, .logouts, .logonProblems: connections
  • stats.sessions[].silences, .longestSilenceMs: silences
Read the API documentation

Questions

How do I check FIX session health from a log?
Load the log in the analyzer and open the Analytics view. The Session health panel has one row and one timeline per session, with Heartbeats, Test Requests, sequence gaps, resend requests, gap fills, resets, logons, logouts, silences and the longest silence.
When is a FIX session silent for too long?
When one side sends nothing for longer than HeartBtInt (108). The analyzer flags a silence when the gap between two messages from the same side exceeds HeartBtInt plus 20%, with at least one second of margin.
What is the difference between a gap fill and a sequence reset?
Both are Sequence Reset (35=4). With GapFillFlag (123) Y it is a gap fill: it skips sequence numbers that are not resent, usually admin messages, during a resend. Without it, it is a reset that sets the next number outright and skips recovery.
How does the analyzer detect a sequence gap?
For each sender it expects MsgSeqNum (34) to go up by one. A higher number is a gap, a lower number without PossDupFlag (43) Y is too low. A Logon with ResetSeqNumFlag (141) Y or MsgSeqNum 1 starts again from that number.
Is a Logon with ResetSeqNumFlag a problem?
Not by itself. Many sessions reset sequence numbers at the start of every day with 141=Y. A reset in the middle of the day means the session reconnected and both sides started from 1, so check that no messages were lost around it.

Reference

More of what the analyzer measures

  • Message rate: Messages per second, peak second and busiest minute, mix by MsgType and category, inbound and outbound.
  • Order outcomes: How every order ended: filled, partly filled, cancelled, expired, rejected or open, with fill, reject and cancel rates.
  • Execution analysis: Execution Reports by ExecType (150), partial and full fills, and the fill-size distribution from LastQty (32).
  • Trading activity: Volume and traded value per symbol and currency, side, order type, time in force, venues, order-to-trade and cancel-to-fill.
  • Order latency: New order to ack, first fill, cancel and replace times, and Test Request to Heartbeat: p50, p90, p99 and max.
  • Reject analysis: SessionRejectReason, OrdRejReason, CxlRejReason and BusinessRejectReason ranked, Text (58) grouped, CheckSum and BodyLength errors.
  • FIX Log Analyzer: every statistic, how privacy works and how to start