The abort guard that lied: a verification check that returned {} and refused a valid send
The abort guard that lied
2026-08-30 · automation · 5 min
We run an agent that submits proposals on a freelance marketplace through raw Chrome DevTools Protocol. Before it clicks the final send button, an abort guard reads the form back and refuses to submit unless every field checks out: the price, the deadline, the confirmation checkbox, and a minimum length on the message. The guard exists so that the agent can never fire blind.
This week the guard did its job perfectly — and it was wrong.
## What happened
The target project sat behind a 24-hour exclusivity gate, so the script polled the page until the gate lifted, then filled the form. The log read:
poll 39: exclusivo=False botao_visivel=True
abrir form: True
pre-submit: {}
ABORTED: form incomplete — nothing sent
Every field had just been filled. The guard saw none of it.
## The bug
The guard was written as an arrow function expression and passed to Runtime.evaluate:
pre = pg.ev("""()=>[document.querySelector('#oferta')?.value,
document.querySelector('#confirmar-envio-proposta')?.checked, ...]""")
evaluate runs an expression. A bare arrow function is a perfectly valid expression — its value is a function object. Serialized with returnByValue, a function becomes {}. The guard didn't fail to run; it ran to completion and returned the function itself, un-invoked. One missing pair of parentheses.
pre = pg.ev("""(()=>[...])()""") # invoke it
Fixed and re-run: pre-submit: ['900', '10', True, 1356] — guard green, send fired, the marketplace's own "cancel / improve proposal" controls confirmed the submission, and the panel now lists the proposal with the exact price and duration the guard verified.
## Why this one matters
- **The safe direction of a guard still lies.** Aborting when nothing was wrong costs a real opportunity: the exclusivity gate had just opened, and a competitor's proposal sent during our false abort gets read first. Fail-safe is not the same as fail-correct.
- **{} was honest output, not an error.** No exception, no stack trace — the code returned the truthful serialization of a function object. Bugs that produce silence-shaped data are the ones that pass review.
- **Verify the verifier.** The right test for a guard is to run it against a known-good state and assert it says *yes*. We had only ever tested the refusal path (empty form → abort). The approval path had never fired a true positive before it needed to.
## The rule we took
Any guard whose expression is handed to an evaluator gets its result asserted against a positive control once, in a dry run, before it is trusted to gate a real action. If your verification step has never returned "go" on a state you knew was good, you don't have a guard — you have a coin that only ever lands on "no".
Measured data from this incident: the 24-hour exclusivity gate opened at ~09:35 local, 39 polls (~39 min) into a poll loop started at 08:56 — consistent with, and slightly later than, the ~24h-after-publishing clock measured in [an earlier post](/exclusive-project-gate-22-hour-clock.html). All figures are from our own logs; nothing here is sponsored or affiliated with any marketplace.
Lab output & tools
Originally published on the Oroboro Labs blog.
Top comments (0)