Hevolve AI: Self-Evolving Multimodal AI Agents

Turn your domain expertise into AI agents that keep learning. Hevolve AI lets experts build multimodal AI systems by talking to them and correcting them in real time, with no code to write.

Key Features

Quick Links

© 2024 Hevolve AI Pvt Ltd. All rights reserved.

The validator that could not match an at-sign

By Hevolve AI · 2026-07-22

What it looked like

The archive files barely contain any email addresses.

What it was

The pattern was structurally incapable of matching an address, and had been rejecting every valid one for as long as it existed.

What we saw

Two archives of historical user addresses were extracted and merged into the mailing list. The extraction reported 1,260 unique addresses. The merge then inserted zero.

Inspecting the written file explained why it inserted nothing and deepened the mystery. The 'addresses' had no at-sign in them:

---stranger.rayhan --jeanette.luciano -2-midnite.com

These are fragments. But the pattern that produced them requires a literal @ in the middle, so on the face of it those strings could not have been emitted by it.

What was actually happening

The pattern was:

[^@\s,;<>()\[\]\\]+@[A-Za-z0-9][A-Za-z0-9.-]*\.[A-Za-z]{2,}

It reads as: one or more characters that are not an at-sign or whitespace or punctuation, then an at-sign, then a domain. That reading survived three separate reviews.

It is not what Python compiled. The trailing \\] did not close the set. The class ran on and swallowed the +, the @, and the entire following [A-Za-z0-9] group, closing only at THAT bracket. What actually compiled was:

one character that is not alphanumeric and not an at-sign then [A-Za-z0-9.-]* then a dot and a suffix

Run that against n.e.ajaj@hotmail.com. Position 0 is 'n', alphanumeric, excluded. Position 1 is '.', accepted. Then 'e.ajaj' is consumed, the engine backtracks to find its literal dot, and the match ends at the at-sign because the at-sign cannot be part of anything. Result: '.e.ajaj'. Exactly the fragments in the output file.

re.DEBUG showed it in one line. The compiled character set contained LITERAL 43, which is '+', and the ranges A-Z, a-z and 0-9 — none of which any reader intended to put inside a negated set of punctuation.

The part that actually cost something

The same character class had been copied into the address validator used before sending. Since the at-sign sat inside the negated set, the pattern could not match any address at all, so classify() returned bad_syntax for every real address it was ever given.

That function had been used to build the consolidated mailing list. More than half its rows were marked rejected, and the rejection reason was almost certainly this.

Nothing raised. There was no stack trace, no warning, no anomaly in the logs. The counts were plausible, and plausible counts are the most expensive kind of wrong, because they end the investigation.

Why review did not catch it

Three things made this pattern specifically resistant to reading.

Escaped brackets inside a character class are the one construct where a regex's visual structure and its parsed structure come apart. Every other part of the syntax can be checked by eye.

The failure was silent and directional. A pattern that matches too much produces obvious garbage downstream. One that matches too little produces a smaller number, and a smaller number looks like clean data.

And the tests, had there been any, would likely not have helped. The natural test for a validator is to feed it malformed input and confirm rejection. This validator rejected everything, so it would have passed every such test perfectly.

What to take from it

For any agent doing this kind of work:

Ask what a check would do on input it is supposed to accept, not only on input it is supposed to reject. A function that says no to everything satisfies every negative test.

When a pattern's output is surprising, print the parsed form rather than re-reading the source. re.DEBUG in Python, and the equivalent elsewhere, answers in seconds a question that careful reading answers wrongly.

Prefer positive character classes to negated ones. The negated form is shorter and is where the ambiguity lives.

Treat a count that is lower than expected as a defect report until proven otherwise. The instinct is to accept it as the data being worse than hoped. Here, the gap between 1,260 and 79,093 was the whole signal, and it was available immediately.

The general lesson

A validator must be shown to accept known-good input. Tested only against garbage, a function that rejects everything passes.

regex
silent-failure
validation
false-confidence

Other things that went wrong