chrome-extensions.sgit.ai / silent failures

The six silent failures

Every one of these is invisible by construction: it does not throw, it does not error, and the dashboard keeps looking fine. In this kind of work — reading an undocumented target's own traffic — the expensive bugs do not throw. They return something reasonable-looking, and you find out weeks later when a number does not match a screenshot. This is the page to link when someone asks why the guides insist on so much ceremony.

#FailureLooked likeCost
1Running an old buildA quiet week on the target10 days
2A limit that selects against your evidenceA clean, confident, wrong conclusion3 weeks
3A boundary that fails only when data gets bigWorked for months, then a corrupt filea 113 MB export written as the word "undefined"
4A pattern written against one exampleSilent under-collection, no error anywherematched 2 of the next 5 cases
5A test file nothing importsGreen suite, zero assertions2 versions passing vacuously
6Deriving a fact from the thing it points atPlausible timestamps that are all wrongevery reaction dated to the post's birthday

A seventh appears in guide 02 §3: responseText throwing on arraybuffer/blob responses — symptom a rising "matched" count with zero "captured", cost three capture runs.

1. Running an old build

Files on disk were updated twice; a browser extension keeps running the build it loaded until it is explicitly reloaded. Captures kept coming back as if the new code did not exist — indistinguishable from a quiet week on the target. Ten days before anyone caught it.

Guard: stamp the build into every artefact the tool produces, and show it in the UI on the surface the human opens first, coloured when stale. Full treatment: guide 02 §8, build provenance.

2. A limit that selects against your evidence

The one to lead with, because it is a reasoning failure rather than a coding one. A conclusion rested on 70 payloads, "59 of which were whole rather than truncated" — and the 11 truncated ones were every payload big enough to contain the answer. They were whole because they were small.

"59 of 70 were whole" and "the 11 that were not are the only ones that could have held the answer" are the same sentence about the same run, and only one is worth saying.

Guard: whenever a cap, limit, page size or sample boundary is in play, ask whether it selects for the thing being tested. Full treatment: guide 01 §2, guide 03 §7.

3. A boundary that fails only when data gets big

A download path sent export bytes over chrome.runtime.sendMessage, which cannot deliver anything past its size limit. The callback fired with undefined; a normaliser turned "no reply" into {}; the guard against a falsy value never fired; and new Blob([undefined]) does not throw — it stringifies, writing a file called undefined.json containing the word "undefined". Worked for months, until one run's export reached 113 MB.

Guard: any channel with a size limit is a landmine on a path whose data grows — move the boundary, don't widen it. Full treatment: guide 02 §7.

4. A pattern written against one example

A pattern written against a single captured example matched two of the next five cases. One "variant" was a genuine spelling difference; another was a different type of object arriving through the same endpoint — two mistakes wearing the same coat, needing opposite fixes.

Guard: when the sample is a structured identifier with a type in it, read the type as data rather than baking it into the pattern. Full treatment: guide 03 §7.

5. A test file nothing imports

The runner used to have a hand-maintained import list. A new test file sat there for two versions, passing vacuously, because nothing imported it — a green suite with zero assertions actually running.

Guard: auto-discover test files and exit non-zero on zero found. "No tests found" and "all tests passed" must never look the same. Full treatment: guide 01 §5.

6. Deriving a fact from the thing it points at

An early version read the wrong identifier and dated every "user reacted to this" event to the post's birthday rather than the reaction's — the payload had both an outer event identifier and an inner content identifier, and they can be months apart.

Guard: verify every derived fact against an artefact the system itself produced; never trust a derivation on plausibility alone. Full treatment: guide 03 §3.

The pattern underneath

None of these six throw. Each one produces a plausible-looking result that is quietly wrong, and the fix in every case is the same shape: stamp provenance, verify against independent evidence, and count what you did not capture rather than staying silent about it. See the day-one checklist for the smaller details that prevent these, and the 34-check rubric for how to grade whether an extension is protected against them.