The race condition, explained

A follow-up job built the day before, scheduled to fire at a fixed time, is working from a snapshot of lead state that can go stale. If the recipient replies at 16:58 and the job fires at 17:00 without re-checking, the system sends a follow-up to someone who already responded — the exact outcome the sequence exists to avoid.

Why the state check has to happen at send time

Checking reply state only when the sequence is first scheduled catches nothing that happens afterward. The safest design re-reads current lead state as the last step before the message actually goes out, as close to send time as the pipeline allows, rather than trusting whatever the state was when the job was queued.

How reply detection actually works

Most systems detect a reply either by polling the connected mailbox over IMAP for new messages matching the thread, or by receiving a webhook from the sending provider when a reply lands. Either way, matching is normally done against the original Message-ID and References/In-Reply-To headers, not just sender address, since a recipient can reply from a different address or forward the thread.

Walking through the 16:58 scenario

The follow-up job was prebuilt the day before and set to fire at 17:00. The reply arrives two minutes earlier, at 16:58. If the send function has no final read of lead state, it fires anyway. If it queries current state immediately before sending, it sees the new reply and cancels — the two-minute window is exactly why a same-day, at-schedule-time check exists.

Building the decision gate

The practical fix is a check-before-send pattern: the function responsible for actually sending the follow-up queries the lead's current reply status as its own first action, separate from and later than whatever check happened when the job was originally scheduled. If the state has changed to 'replied' by then, the send is skipped and logged as cancelled, not silently dropped.

What if detection itself lags

IMAP polling on an interval, or webhook delivery from a provider, both carry some latency — usually seconds to a couple of minutes, not hours. A small buffer window where a reply might not yet be visible at send time is an acceptable tradeoff against the alternative of blocking every send pending a guaranteed-instant reply check, which isn't achievable with most mailbox integrations.

Handling ambiguous or partial replies

Not every inbound message is a clean signal. An auto-forwarded copy, a one-line 'please remove me' that isn't a formal unsubscribe click, or a reply that's clearly misdirected all need to cancel pending follow-ups even though they don't fit neatly into a simple 'interested/not interested' bucket. The safe default is that any inbound message tied to the thread cancels the sequence — sorting out what kind of reply it was can happen afterward, but the send should already be stopped.

Multi-channel sequences raise the stakes

If a sequence also includes a LinkedIn message or a phone call step alongside email, reply state needs to be shared across whichever system triggers each channel. A prospect who replies by email but then still receives a LinkedIn touch scheduled from a separate, unsynced system experiences the same problem this guide describes — just across a different pair of channels instead of two emails.

Testing the gate deliberately before relying on it

Before trusting this logic in production, it's worth deliberately triggering the exact race condition — sending a reply to a test sequence seconds before a scheduled follow-up — and confirming the send is actually cancelled and logged correctly. A gate that looks correct in code review can still fail under real timing conditions if the state check queries a cached value instead of live data.

This kind of test is cheap to run and catches a class of bug that's otherwise invisible until a real prospect receives an awkward follow-up right after replying — at which point the cost is a damaged impression with an actual contact, not just a failed test.

The underlying principle

Anything that can change a lead's state between when a job is scheduled and when it actually executes needs a fresh check immediately before that execution, not just at scheduling time. This applies beyond reply detection too — the same logic covers unsubscribe requests, bounce events, or manual status changes made by a team member. Reply detection is simply the most common version of this pattern in an outbound system.

It's worth noting this same discipline applies just as much to smaller-scale manual outreach as to a fully automated system — even a person manually queuing a batch of follow-ups the night before should get in the habit of a quick inbox check immediately before hitting send, for exactly the same reason.

None of this requires sophisticated infrastructure to get right — even a simple, reliable last-second state check is enough to close the gap that causes this problem, and it's worth prioritizing over more visible feature work precisely because the failure mode is invisible until a real prospect experiences it firsthand.

Field checklist

  • Re-check current reply state immediately before sending, not only when the follow-up was originally scheduled.
  • Match replies against the original Message-ID and References/In-Reply-To headers, not sender address alone.
  • Treat any detected reply as an immediate cancel signal for all pending follow-ups in that sequence.
  • Log a cancelled send explicitly, rather than letting it disappear silently from the queue.
  • Accept a small detection-latency buffer window as a reasonable tradeoff, not a reason to block sending entirely.
  • Test the exact race condition deliberately: trigger a reply seconds before a scheduled send and confirm it's suppressed.

Primary sources

Standards and provider policies can change. These links are the reference points used for this field note.

  1. RFC 9051 — IMAP4rev2IETF / RFC EditorMailbox flags, UIDs, BODY.PEEK and IMAP4rev2 behavior.
  2. Email sender guidelinesGoogle Gmail HelpAuthentication, TLS, DNS, spam-rate and bulk-sender requirements.