What broke
Missing fields and empty results rendered as «all is normal»
On 5 August 2026, Claude's own status tool read a field that didn't exist and reported a crashed trading cycle as calm. The fix brought two false alarms the same evening. The rule we took from it: absence must never be shown as «all ok».
The short version
What went wrongClaude's status tool read a field that did not exist and printed "skipped: (no)" for a crashed cycle. The first fix then used a guessed list of normal verdicts and raised two false alarms.
How we found itThe crash only showed because the raw output was run by chance. The false alarms came up in the same evening's checks.
The rule nowNever show absence as all ok. Use a typed map over every verdict value so a new value breaks the build, and give abnormal states an exit code.
This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes
This happened in the stock app, the part that runs a trading cycle against a broker account. On 5 August 2026 a small command-line tool told us a trading cycle had gone fine. That cycle had crashed. Later the same evening, the fixed tool raised two false alarms about cycles that really had gone fine. Both mistakes are Claude’s, and the commit messages say so.
The commit messages are in Norwegian. The quotes below are our translations.
The tool that was supposed to catch problems
The tool prints a summary of the latest live trading cycle: when it ran, how many orders it placed, how many were blocked, and whether the cycle was skipped. We use it to check that the trading cycle is still running.
It read a field called skipped, and that field does not exist. The cycle status is stored as a verdict plus a text. When the tool found no skipped field, it fell back to a default: «(no)». So for a cycle whose verdict was skipped-abnormal, which means it crashed, the tool printed «skipped: (no)».
The fix commit (17:59 that day) describes what happened next, in Claude’s first person:
«I read “0 orders placed, 0 blocked, skipped: (no)” as calm, while the 17:30 cycle had died on a missing column.»
The cycle had crashed because deployed code wrote to a database column that didn’t exist yet. The migration that should have created it had never run (more on that below). The tool turned that crash into what the commit calls «a reassurance that everything was normal».
How it was found
Nothing in the setup caught it. The commit says plainly: «The error only became visible because I happened to run –raa.» --raa («raw») is the tool’s flag for dumping the stored status as plain JSON, without the summary. The raw output showed the verdict the summary had hidden.
So it was luck, not a check. The commit also names the deeper problem: you could only trust the tool if you already suspected something and knew to look at the raw data.
The first fix
The 17:59 commit made the tool read the verdict instead. Any abnormal verdict is now printed loudly together with the error text. A halted account is shown. The tool exits with code 2 on an abnormal cycle, «so it can be used in a check instead of having to be read by someone who already suspects something.»
It also separated two cases that had looked the same in the summary: «0 orders» because there was nothing to do, and «0 orders» because the cycle never got that far.
The overcorrection: two false alarms
To decide which verdicts counted as normal, the first fix used a hand-written list of good values. That list was a guess. The next commit, at 20:33, tells the rest:
«Two errors in a row, both mine. First I whitelisted ‘ok’ and ‘ran’, and it shouted about
traded. Then I added four GUESSED names, and it shouted aboutevaluated.»
evaluated was, in the commit’s words, «a perfectly normal cycle that evaluated four positions and found nothing to do.» traded is a cycle that did its job. The tool now raised alarms about normal operation.
The commit puts the root cause in one sentence: «The root cause was not the list, but that I kept my OWN list next to the producer.» The code that classifies a cycle produces exactly four verdicts: evaluated, traded, skipped-normal and skipped-abnormal. Only the last one is abnormal. The tool had been guessing at values that another module already defined.
The commit links this to a failure pattern already written up in the project’s notes, which it calls the allowlist blind spot: a hard-coded list decides what a layer can see, and new values don’t get checked. «The symptom is usually silence; here it was the opposite — false alarm — but the mechanism is the same.»
The second fix
Instead of a list of strings, the tool now uses a typed map that covers every value of the verdict type and marks each one as normal or abnormal. If the code that produces verdicts gains a new value, the map is incomplete and the build fails, so the new value can’t slip through unchecked. A verdict the tool doesn’t recognise, for example from an older stored status, is still treated as abnormal.
The same commit records what was checked after deploy: the 20:30 cycle came back evaluated, with four positions evaluated and all within threshold.
Third of four that day
The tool bug wasn’t a one-off. The 17:59 commit calls it «the third occurrence of the same error class in one day, and this time in the tool that was supposed to uncover the other two.» A summary document written that evening lists four failures, all of the same kind: something missing was shown as confirmation that all was well.
- A hand-written migration was never registered in the migration journal. The migrate command skipped it and printed «Migrations completed». That is what crashed the 17:30 cycle.
- Reconciliation read a column that didn’t exist. A try/catch that Claude had added for robustness caught the error and fell back to a default, so the mirroring «looked like it worked».
- The status tool read a missing field and printed «(no)».
- The tool’s own list of normal values, kept next to the producer, gave two false alarms.
According to the same document, none of these were caught by 933 passing tests. It gives three reasons. Raw SQL is not type-checked. The simulated broker account was empty, so there was never a difference to mirror. And unit tests test what exists. In the document’s words: «none of them can detect that something does not exist.»
What changed
- The status tool shows an abnormal verdict loudly, with the error text, and exits with code 2, so a script can use it as a check.
- «Nothing to do» and «never got there» are no longer shown the same way.
- Which verdicts count as normal is defined by a typed map over every value the producer can emit. A new value breaks the build.
- Unknown verdicts still count as abnormal.
- A test now checks that the migration journal and the migration files match in both directions.
The rule
Absence must never be shown as «all ok». If a field, column or result is missing, say that it’s missing. Don’t fill the gap with a default that looks like an answer.
Don’t keep your own list of another module’s values. Use a map over every value of its type, so a new value breaks the build instead of slipping through. Write abnormal states clearly and back them with an exit code, so a check can catch them before someone has to suspect something.
And don’t overcorrect. A guard that raises alarms about normal operation will be ignored. We learned that twice in one evening.