Why your vibe coded app shows wrong numbers and no error
I was auditing a vibe coded app that computed one important number for every record, and I found the same calculation written four separate times, in four different files, with slightly different constants in each. That alone is a mess worth fixing. But the thing that stopped me cold was smaller and quieter. One of those four versions, when its input did not parse, did not throw. It returned fifty percent. A clean, reasonable-looking fifty percent, handed straight to the dashboard as if it had been computed.
If your vibe coded app is showing wrong numbers and no error, the cause is almost always this: somewhere a calculation that could not do its job returned a plausible default instead of failing. The app did not crash because it was never allowed to. A failure got dressed up as a result and passed downstream, and now you cannot tell which numbers are real and which were invented, because they all look the same. The bug is not the wrong value. The bug is that the wrong value arrived silently.
why does my AI app show wrong numbers with no error?
Because the code caught its own failure and answered anyway. A generated calculation often ships with a defensive fallback baked in: a try that swallows the parse error, an "or" that supplies a default when the real value is missing, a ternary that quietly resolves to zero or to some midpoint when the input is malformed. Each of those looks like good hygiene in isolation. None of them throws. So when a record comes in wrong, the function does not stop and tell you, it substitutes a number and keeps going.
That substitution is the whole problem. A crash is loud and honest. It points at a file and a line and says "I could not do this." A default is quiet and dishonest. It says "here is your answer" while hiding the fact that it never actually computed one. In my case, fifty percent was the worst possible choice, because it is exactly plausible enough to survive a glance. If the fallback had returned nine thousand percent, someone would have caught it in a day. Fifty percent just blended into the dashboard and became part of the picture everyone trusted.
what makes a silent default the most expensive kind of bug?
It fabricates data that looks real, and fabricated-but-plausible data is nearly impossible to find after the fact. Once a made-up value lands in a record, it is indistinguishable from a computed one. There is no flag on it, no error in the log, no visual tell. You cannot grep for it. You would have to re-derive every number from scratch to know which ones the app invented, and by then the invented ones have been feeding reports, decisions, and whatever the next stage of the pipeline built on top of them.
This is the pattern I think about as failure-surface-first: you design the failure mode with the same care you give the happy path, and the first rule is that a failure has to be visible. A function that cannot compute its answer should fail loudly, throw, return an explicit "unknown," halt the record, anything that surfaces the fault. The one thing it must never do is return a value that looks like success. On a vibe coded project this matters more than usual, because you did not write the fallback yourself, the generator did, and generated code loves a defensive default. It is trying to be robust. What it actually built was a machine for quietly making things up. That is the quiet cost of vibe coding at speed: the fallback you never saw is still yours to answer for.
how do I stop my app from inventing data?
Go find the fallbacks and make each one choose between a real value and a loud failure, with nothing plausible in between. The move is to hunt down every place a calculation can resolve without actually computing: the swallowed try, the default on the right side of an "or," the ternary that picks a midpoint when parsing fails. For each one, ask a single question. When this branch fires, is the number it returns real, or is it a placeholder wearing the costume of a real number? If it is a placeholder, it has to become an error or an explicit unknown instead, so the failure reaches you instead of the dashboard.
Then collapse the duplicates. The reason my app had a silent fifty-percent default in one place and correct behavior in three others is that the same calculation existed four times, and only one copy had rotted. One source of truth for that number would have made the bug impossible, because there would have been exactly one place for the failure to live and exactly one place to make it loud. Four implementations of the same formula is not just messy, it is four independent chances for one of them to start lying while the others stay honest. If you want the deeper version of that story, I wrote about why fixing one bug keeps breaking another when the same rule lives in several places.
The tell to internalize is this. Any time a calculation in a vibe coded app can return a normal-looking value along a path where it did not actually succeed, that path is a place the app can lie to you, and it will do it silently, and you will trust it because it looks exactly like the truth. Build the failure so it cannot hide.
questions that keep coming up
How is this different from a bug that returns the wrong answer? A wrong answer usually comes from wrong logic you can find and fix. This is worse, because the logic "worked," it just worked on a failure. The value is not miscalculated, it is fabricated, and there is no error trail pointing back to it. You are debugging a lie the app told confidently, not a mistake it made openly.
Won't making everything throw just fill my app with crashes? It surfaces crashes that were always there, hiding as fake data. That is the point. Better to see a hundred records fail loudly on bad input than to ship a dashboard where an unknown fraction of the numbers were quietly invented. Once the failures are visible, you can decide what each one should do, an explicit unknown, a skipped record, a flagged row, on purpose instead of by accident.
How do I even find these in an AI-built codebase? Search for the shapes: a default after an "or," a try that catches and returns something, a ternary that resolves to a round number on the failure branch. Every one of those is a small decision to answer instead of fail, and on a vibe coded build there are usually more of them than you expect, because a generator reaches for a safe default the way a person reaches for a comment.
If you are shipping a vibe coded app and you are not sure how many of your numbers were actually computed, /work-with-us. Send me the calculation you rely on most and I will show you where it can fail silently, so the app tells you when it does not know instead of making something up. Work with VibeKoded.
The four implementations were the mess. The silent fifty percent was the danger. A vibe coder who makes every failure loud will lose a few evenings to crashes that used to hide, and gain back the one thing that actually matters, which is knowing that the numbers on the screen are the ones the app really computed.
// part of the custom apps topic
// grab the free starter kit that makes your AI stop forgetting and stop guessing: get it →
// building with AI? the field manual has the structured lessons.
// hitting this on a real build? this is what I fix →