---
title: "Why 499 search events on July 24 meant there was no code to write"
canonical: https://dxdev.com/blog/verify-symptom-not-commit/
datePublished: 2026-09-26
---
GA4's `view_search_results` report was logging about 205,000 site-search events a day, and a chunk of them weren't searches at all. The query strings looked like probe traffic: SQL fragments, encoded payloads, strings nobody types into a search box on purpose. The ticket, tagged "search spam" with nothing else attached, landed in the queue with no other context.

## The code hunt

My first read was that something passes raw querystring input into the search-tracking call without sanitizing it, and the garbage ends up in analytics. That's a code problem, so we went looking for the code problem.

We opened `www/` and grepped for where the site fires the search-results event, tracing from the search page down to the `gtag()` call that reports it. We found it. The page reads an `s` querystring parameter straight off the URL and hands it to GA4 as the search term, with no filtering. Anyone who wants a garbage string sitting in an analytics report only has to hit `/search?s=<whatever>`.

That's a real hole, and it looked like the whole story. We started drafting a patch: validate `s` before it reaches the tracking call, and drop anything that doesn't look like a plausible query.

## The check that stopped the patch

Before opening a branch, we ran the check we should have run first. Is the symptom still happening right now? We pulled GA4's Admin API config for the property and found that `searchQueryParameter` no longer listed `s`.

Someone had gone into GA4 Admin, before the ticket was even triaged, and removed `s` from the set of parameters GA4 treats as a search-query trigger. It was a config change made outside the repo, and nothing in a diff or a commit log could have shown it to us.

With `s` out of that list, GA4 stops treating hits to that URL as search events regardless of what value sits in the parameter. Our fix would have sanitized a value GA4 had already stopped reading. Shipping it wouldn't have been wrong exactly, since it's still a reasonable hardening of an unvalidated input. But it would have closed the ticket on the strength of a diff that had nothing to do with why the symptom stopped. If the timing had lined up differently, and we'd shipped the patch a day before the config change, we'd have taken credit for a fix we didn't cause and had no way to know it.

## Looking at the signal itself

So we checked for the thing the ticket was about, not the thing we'd already started fixing. There were two questions: is a second code path in `www/` still emitting this event unfiltered, and does the GA4 data show the symptom gone?

The first was quick. One grep across the repo for other places reading `s` into a tracking call turned up nothing. The second mattered more. We pulled the daily `view_search_results` counts and looked at the shape of the collapse. The baseline ran around 205,000 events a day. On July 24, the day we checked, it was 499. That's better than a 99% drop, and it's a step function rather than a taper, which matches a config flip and not a code rollout working its way through caches and stale bundles.

That's the difference between "the code that plausibly caused this got changed" and "the thing the customer complained about stopped happening." The first is a diff you can point to. The second is the only thing that closes a ticket. We had the diff half-written before we looked at the second one.

We [closed the ticket as a no-code fix](/blog/close-gate-ops-work-no-git/): JIRA issue type set to NO CODE, transition 1021, story points 1.0, sprint 554. No branch and no commits, because there was nothing to merge and the fix had already happened somewhere we don't have write access to. The `s`-parameter hole in `www/` is still open as its own hardening item, worth doing, just not what this ticket was about.

When a ticket describes a symptom, pull the current data first and see whether the symptom is still there. Close the ticket on that evidence, and leave the diff out of it.
