A contact form quietly failed my automation for four days. The blockers were a consent anchor, a trusted-mousemove flag, and a dwell timer.
This is a field report from November 2026, based on one debugging session on 6 November. The site involved can change its checks at any time, so treat the specifics as a snapshot of that session, not a stable description of how the site behaves now.
Every evening, one of my scheduled jobs pitches a few of my sites to third-party listing and catalog sites through their public contact forms. On 2 November, a directory called edit.org started failing. The form began accepting submissions again only after I fixed three separate blockers: a consent control my selector never clicked, a mousemove check that only counts trusted events, and a minimum-time check that rejects fast submits. The rest of this post is the four days it took to find them.
The failure looked like their outage, so I blamed their outage
The frustrating part of this class of bug is that it produces no error. No HTTP 403, no CAPTCHA challenge, no “access denied” page. The form just never fires. For four days my pipeline logged FAIL for this channel, and because the failure was silent, I filed it under “site’s problem, maybe their form is broken.” I have written about this exact shape of mistake before — silent failures in unattended automation — and I still made it again. When a scheduled job fails the same way twice, the first question should be “what is the page doing?”, not “what is wrong with the site?”
Separately, that same week, I had to roll back falsely recorded successes on a different channel after its post-submit check turned out to match static page text. Two channels, opposite failure directions, same root cause family: my own checks were asserting things the page never confirmed.
My button-only selector missed the consent anchor
The page greets you with a cookie consent banner that overlays the lower part of the viewport, and the contact form sits behind it. For days my script’s dismiss step ran without error and did nothing. The reason: my selector only searched for a <button>, and the banner’s accept control is an <a> element. The lookup found zero elements, and the code treated zero matches as “nothing to dismiss” instead of “something is wrong.” The banner stayed up for the whole session.
The fix was to drop the tag restriction: find any element whose text matches /accept cookies$/i and click that. One line. The banner disappeared and the form became fully visible. But the submission still did not go through, because the page had a second blocker waiting.
The mouse check only counted events the browser considered trusted
The page carries an inline anti-bot check built around a variable I will paraphrase as mouseMoved. The submit handler asks, in effect: did this visitor’s mouse actually move across the document while the form was open?
Two things make this check nastier than it sounds. First, it only counts events with isTrusted === true. That flag is set by the browser for input the browser itself generated, and JavaScript running on the page cannot set it — dispatchEvent() always produces untrusted events, by design. My old script synthesized mousemoves by dispatching JS events on the document. They ran, they were just permanently untrusted, so the flag stayed false. No error, no hint, just a form that declines to send.
Second, even the “real input” path had a trap in my setup. I drive the browser over the Chrome DevTools Protocol, and CDP’s Input.dispatchMouseEvent does produce events the browser treats as trusted. But I run several channels through one browser instance, and the edit.org tab sat in the background while other tabs worked. In that session, my document-level mousemove listener did not register the dispatched trajectory until I brought the tab forward with Page.bringToFront and replayed the mouse path again. Whether background tabs drop these events in general depends on the browser version and event type; what I can attest to is this session, this setup, before and after foregrounding.
The working sequence is now fixed in the script: bring the tab to the front, then dispatch a short mouse trajectory across the document. Only then does the flag flip.
Seven seconds was too early; 25 seconds went through
Even with the banner gone and the mouse flag set, quick submissions still bounced. The page has an inline dwell check: it records a start time at load and compares the elapsed time against a minimum at submit. Rushing the form earns a ”✋ Wait…” message and a refusal.
That evening I tested two points instead of guessing: a submit 7 seconds after page load was rejected, and the same submission at 25 seconds was accepted. With the other checks satisfied, those two tests bracket a fixed dwell threshold between the rejected 7-second attempt and the accepted 25-second attempt; they do not identify the exact cutoff. Since that run, I have simply waited 30 seconds before submitting. If I ever need the exact number, the inline script on the page has it — that is where the start time is read from, not from any server round trip.
The revised script reached the confirmation page that evening
The timeline compressed to three minutes. At 21:47 on 6 November, the old script made one last attempt with synthetic mouse events and the form did not send. At 21:50, the revised script — tag-agnostic banner click, foreground tab, trusted mouse trajectory, 30-second dwell — submitted the same pitch for the same site and reached the directory’s ?thanks=1 confirmation URL, which I record as this channel’s submission-success signal. I kept the confirmation screenshot and the ledger entry, and the sites that had queued behind this channel for days went out the same night.
The other change I shipped that evening was diagnostic, and worth more than the fix: the pre-submit check now treats “selector matched zero elements” as a hard failure with a screenshot, instead of a silent skip. The four lost days were not caused by three clever anti-bot tricks. They were caused by my own code declining to tell me it had found nothing.