ZagTrader

Sequence numbers and recovery

PossDupFlag (43) and PossResend (97): handling duplicate messages

PossDupFlag=Y marks a message replayed by the session layer with its original sequence number. PossResend=Y marks a message the application sent again under a new sequence number. They need different duplicate checks.

What it means

43=Y is set on messages replayed in answer to a ResendRequest. The message keeps its original MsgSeqNum (34). The receiver checks the sequence number: if it has already processed that number, it ignores the message. A replayed message must also carry OrigSendingTime (122), the SendingTime of the first transmission.

97=Y is set by the application when it is not sure an earlier message arrived and sends the same content again with a new sequence number. The session layer cannot detect this duplicate. The receiver must check business identifiers such as ClOrdID (11) for orders or ExecID (17) for executions.

Both flags are header fields. A message can carry both if a PossResend message is itself replayed later.

Common causes

  • Reject with 373=1 and 371=122: a message with PossDupFlag=Y arrived without OrigSendingTime.
  • Reject with 373=10 followed by a Logout: OrigSendingTime is later than SendingTime, usually because the replay copied the new time into 122 or kept the old time in 52.
  • Orders executed twice: the receiver processed a PossResend order as new because it did not check ClOrdID.
  • Fills booked twice: the buy side ignored PossDupFlag on a replayed Execution Report and did not check ExecID.

How to fix it

On the side that replays or resends

On the side that receives

  • For PossDupFlag=Y: if the sequence number was already processed, drop the message. If not (it fills a gap), process it, but still check business identifiers.
  • For PossResend=Y: look up the ClOrdID or ExecID. If you have it, do not act again; reply with the current state instead.
  • Never treat either flag as a reason to reject the message.

Example

The same fill sent twice: first replayed by the session layer, then resent by the application.

  1. 1. Session replay: original MsgSeqNum 412, PossDupFlag and OrigSendingTime

    8=FIX.4.4|9=202|35=8|49=SELLSIDE|56=BUYSIDE|34=412|52=20261020-11:02:10.500|43=Y|122=20261020-11:01:58.230|37=OID-778901|11=ORD-1010|17=EXEC-5512|150=F|39=2|55=VOD.L|54=1|38=2000|32=2000|31=72.14|151=0|14=2000|6=72.14|10=252|
  2. 2. Application resend: new MsgSeqNum 430, PossResend, same ExecID

    8=FIX.4.4|9=176|35=8|49=SELLSIDE|56=BUYSIDE|34=430|52=20261020-11:03:00.010|97=Y|37=OID-778901|11=ORD-1010|17=EXEC-5512|150=F|39=2|55=VOD.L|54=1|38=2000|32=2000|31=72.14|151=0|14=2000|6=72.14|10=023|

ExecID EXEC-5512 is the same in both. A receiver that books by ExecID books the fill once.

SOH (0x01) is shown as |. BodyLength (9) and CheckSum (10) are calculated as if every | were SOH.

Open this example in the parser

Questions

Should I execute an order that arrives with PossDupFlag=Y?
Only if you have not seen that sequence number or that ClOrdID before. A replayed order you never received is a real order; a replay of one you already accepted must be ignored.
Is OrigSendingTime (122) required?
It is expected on every message with PossDupFlag=Y. Many engines, QuickFIX among them, reject a PossDup message without it.

Reference

Related errors

All FIX errors · FIX guides