---
title: "The Error Log Showed 32 Rows and We Chased the Wrong Thing"
canonical: https://dxdev.com/blog/plausible-silence-adversarial-required/
datePublished: 2026-08-24
---
The [error log printed 32 rows in one hour](/everyday-ai/dashboard-number-with-no-age-on-it/), and we chased it.

The cluster looked like a production break. It wasn't. [Client-side JavaScript reporting](/blog/error-count-went-up-nothing-broke/) had gone live on 11 Aug, so the counter had a new way to hear about browser failures while the server-side rate stayed where it was. We had taken a newly audible signal for a newly broken system.

That correction should have been reassuring, and it made us more uneasy. The conspicuous failure had a trail. A browser reported an exception, the reporting endpoint stored a row, and the log gave us a count to investigate. The more dangerous path in the control surface had no such obligation. It could look useful while a request never became work.

## The counter had become a witness

Claude followed the alert into `/admin/ajax/ClientErrorLog`. The three-hour results held 14 reports of one missing browser variable and 13 of another, from two IP addresses. The endpoint had a deliberate abuse guard, a 10-minute deduplication window and a rate cap of 30 reports in 60 minutes. The raw client-error rows also showed blank `username` values, and the work log recorded that duplicate suppression had never worked.

Those are ordinary error-handling problems. They produce rows, timestamps, message text and a place to put a guard. Claude could follow them because the system had already admitted something went wrong. We had pointed Claude at the loudest artifact and treated its output as the edge of the problem. That framing was wrong.

The control surface was built around a different promise. One composer is present at every level, so a person can type a request without first deciding which session or work area should get it. An explicit target delivers the request. A ranked candidate asks for confirmation. A request with no ranked destination is supposed to become a captured unit on the root, where it can start a session.

## A working composer can hide a missing handoff

The failure is easy to describe once you can see it. The composer accepts text, and the next state never materializes. There may be no exception in the error log and no bad HTTP status exposed in the interface. The person who typed sees an input field that still works and assumes the work went somewhere.

The source has the exact seam. The composer fetches `/api/capture/roots` to populate its start-work path. If that request is not successful, the code returns. If it throws, the catch also returns. The comment says the composer still works without it, and only the start-work path needs it. That is a reasonable resilience choice if the promise is that text entry keeps working. It is an operational hazard if the promise is that an unassigned thought can become work.

We had not noticed how much comfort we were taking from successful error reporting. We sent the investigation there ourselves, toward the artifact that already knew how to describe itself. A counter proves an error reporter reached a database. It does not prove an unassigned request reached a journal, appeared at the root and kept a route to a session. We treated the first as proof of the second for as long as nothing forced us to test the difference.

The design had already named the safer invariant. The server writes a thought to its journal before it tries to route it, which makes the handoff inspectable. It also gives us a verification target. Enter a request with no session target and no ranked destination, then check that a captured unit appears on the root and stays there until it has an end state. A green page load can't establish that, and neither can a healthy error log.

## The test has to try to lose the work

Passive monitoring asks whether a known instrument is reporting. Here it was. The error spike had evidence, and Claude could reduce it to a cause. A silent path needs a harsher question: can a request take the route with the least explicit structure and still leave a durable object behind?

That check attacks the happy path. It withholds the session identifier. It refuses to accept a ranked suggestion automatically. It uses the condition most likely to leave the system with nothing concrete to point at. The expected evidence is small and specific, a journal entry and a root unit. If either is missing, the composer is a convincing surface over a lost handoff.

This is also why the error log was such a good distraction. It wasn't lying. It was answering the question it was built to answer, and we let that answer stand in for a different guarantee. Claude found the visible failure because we gave it an investigation shaped around visible failures.

## What the 32 rows told us

The 32 rows told us the reporter was alive. The root unit tells us whether a new thought survived. So the check we run is the handoff itself: enter a request with no session target or ranked destination, and confirm a captured unit appears on the root and remains there until it has an end state.
