Building an AI SaaS product: five bugs from our own, and what they changed about how we build yours
Most of what we do at Brillnex Systems is build software for other companies. We also run our own product: ConGreeto, an AI assistant that answers a business's website visitors from the business's own material and turns the conversations into scored leads for its team.
We built it because we kept meeting the same problem on client projects: enquiries arriving after hours, answered the next morning, by which time the customer had gone elsewhere. We've written that story up separately as the ConGreeto case study.
This post is about something less flattering and more useful: what running our own AI product in production has taught us. Building for clients, you hand over and move on. Running your own product, you're the one who finds out at 9 AM that something has been quietly wrong for a week.
Here are five real bugs from ConGreeto, all found and fixed between August and September 2026. They have one thing in common: none of them raised an error. Each returned a success, showed a sensible screen, or looked like a completely different problem.
1. The success message that did nothing
What we saw. An account on an Enterprise plan added their website so the assistant could learn from it. The app said it had worked. Nothing was crawled. The person tried again, four times in a few minutes, each time getting a success response and each time ending up with an assistant that knew nothing about their business.
What it actually was. Across the codebase, a plan limit of 0 meant "unlimited". One function, which trimmed the list of pages to whatever allowance was left, didn't follow that rule. It read 0 as "no pages left", trimmed the list to nothing, and then carried on as if all was well: it recorded the attempt, saved, and returned success with an empty list of jobs. Only uncapped plans were affected, which is why ordinary plans never showed it.
We confirmed it by reading the data, not the code: four saved website entries, four recorded attempts, zero crawl jobs, zero pages.
The fix was a one-line guard (only trim when there is a real limit) plus a regression test that runs an uncapped plan through the same path.
What it changed for clients. Two things. First, special values like "0 means unlimited" are a trap: every new piece of code has to remember the rule, and the one that forgets fails silently. We now check every limit for the zero and empty cases in review. Second, a test that checks the response said OK isn't a test that the thing happened. Our tests now check the effect, such as the rows that should exist afterwards, not just the status code.
2. The checklist that never ticked
What we saw. New accounts get an onboarding checklist. One step, "add your content", never completed for many of them, even though their assistant was clearly answering from their website.
What it actually was. The step was ticked off when a count of the account's documents was above zero. That count was borrowed from the billing code, where it deliberately left out crawled web pages, because counting them there would have charged one crawl against two different allowances. For billing, that was correct. For "has this account added any content?", it was wrong. And the most common way to set up ConGreeto is to point it at your website and upload nothing, so the most common customer was the one who could never finish the checklist.
The fix was a separate count of crawled pages, with the step completing when either count is above zero. (This is also the one fix of the five we shipped without its own regression test. It's on the list.)
What it changed for clients. A number answers one question. "How many documents count towards your plan?" and "does this account have any content?" sound alike and have different answers. When we reuse a figure from another part of a system now, we check which question it was built to answer, and we name functions after the question rather than the data.
3. The missing lead scores
What we saw. When a conversation ends, ConGreeto scores the lead and tiers it Hot, Warm, Nurture or Cold. Some leads arrived with no score at all. The pattern looked obvious: chats the visitor ended themselves had scores, and chats that closed automatically after the visitor went quiet often didn't. So we went looking for the difference between the two closing paths.
What it actually was. There wasn't one. Both paths ran exactly the same analysis. The cause was a single line in the instructions to the model: return no score when there are fewer than three substantive visitor messages. Visitors who press "End chat" have usually had a real conversation. Chats that close automatically are mostly one or two lines from someone who wandered off. So the missing scores tracked conversation length, and only looked like they tracked the closing path.
The fix had two parts. The instruction now says every conversation gets a score, and short ones score low. And because an instruction to a model is a request, not a guarantee, code after the model checks the result: it works out the tier from the score (or the score from the tier) and corrects values in the wrong format. A record can no longer reach the dashboard half-scored.
What it changed for clients. In an AI product, the prompt is part of the specification, and a lot of behaviour that looks like a code bug is written in plain English. We keep prompts in version control, review them like code, and test them with real examples. And anything the rest of the system depends on, such as a field that must exist or a value that must fall in a range, is enforced in code after the model, not just requested from it.
4. The fallback that hid a broken paywall
What we saw. Nothing. That was the problem.
A custom banner on the chat widget is a paid add-on. If an account doesn't have it, the server removes the banner before the widget loads. Anyone checking this by eye saw a banner on a paying account and a banner on an unpaid one, and both looked right.
What it actually was. Two mismatches stacked up. The dashboard saved the banner under one field name, and the paid-feature check removed a different field name. When we measured it, every banner stored at the time was under the name the check didn't remove. So the check had never removed a single banner. And the widget, when it gets no banner at all, shows a stock image, so the slot was never empty and a paid banner and a free one looked the same on screen.
The fix was for the check to remove every field name a banner can be stored under, with a test that covers both.
What it changed for clients. Fallbacks are good for users and bad for verification. A default image, a cached value or a friendly "something went wrong" can make a broken system look healthy. We now verify paid features and permissions by checking the data the server actually sends, not by looking at the screen. And every field gets one name from the start. Once two names are in use, every check has to know about both, and this one didn't.
5. The lead email without the lead's email
What we saw. When a chat ends, the business gets an email with the lead's contact details and the conversation. Some of those emails arrived without the visitor's email address, even when the visitor had typed it into the chat.
What it actually was. ConGreeto saves contact details the moment a visitor gives them, so the address was safely stored. But the end-of-chat email was built from the model's final analysis of the conversation, which often came back with the email field blank. Two parts of the system each had a version of "the lead's email", and the alert used the one that was usually empty.
While fixing that, we found a second problem on the same path: if an email failed to send (for example, because the mail server asked us to slow down), the conversation was still marked as emailed, so nothing ever tried again. And once a conversation was closed, nothing went back to it.
The fix has three parts. The email reads the contact details from the stored lead record, which is the one source of truth. A failed send leaves the conversation in a state that can be retried. An hourly recovery job picks up conversations that stalled, and a log of sent emails stops it sending the same email twice.
What it changed for clients. Every important fact gets one source of truth, and everything else reads from it. And "sent" means the mail server accepted it, not that we tried. Any job that can fail halfway needs a way to resume that can't send things twice.
What these five have in common
Read together, they point to one rule: in an AI product, the dangerous failures are the quiet ones. A crash gets fixed the same day. A success message that did nothing, or a score that's missing a fifth of the time, can run for weeks.
So these are the checks we now build into every client project, not just our own:
Check
Catches
Tests assert the effect (rows, files, emails), not just the response
Bug 1
Zero, empty and "unlimited" cases are tested for every limit
Bug 1
Shared numbers are named by the question they answer
Bug 2
Prompts are versioned, reviewed and tested with real examples
Bug 3
Model output is validated in code before anything depends on it
Bug 3
Paid features and permissions are verified on the data the server sends
Bug 4
One source of truth per fact; one name per field
Bugs 4 and 5
Jobs that can fail halfway can resume, without duplicates
Bug 5
There's another check behind all of them: when something looks wrong, read the data before reading the code. Bugs 1, 3 and 4 were each pinned down by a direct look at what was actually stored, not by reasoning about what the code should do.
We've written up other lessons from the same product elsewhere: what a website crawl gets wrong when building a chatbot knowledge base, and five more in AI MVP development: what to build first, including measuring cost before optimising it and checking permissions when a feature is used, not only when it's set.
See the product these lessons came from
ConGreeto is live, and signup is public. If you want to see what it actually does for a business, the ConGreeto team has written a step-by-step walkthrough of one website enquiry, from the visitor's first message to the lead on the team's board the next morning. There's more on the ConGreeto product page.
Frequently asked questions
What's different about building an AI SaaS product compared with a normal SaaS product?
Part of the behaviour lives in instructions to a model rather than in code, and the model's output varies from run to run. That means prompts need the same version control, review and testing as code, and anything the rest of the system depends on has to be checked in code after the model responds.
What is a silent failure in software?
A failure that doesn't raise an error: an operation that reports success but did nothing, a screen that looks right because of a fallback, or a value that's quietly missing some of the time. They're more expensive than crashes because nobody notices them until the damage has built up.
How do you test an AI product before launch?
With a set of real examples (including cases the product must refuse), tests that check effects rather than responses, validation of the model's output in code, and a first week of reading real conversations. We cover the order to build these in, in AI MVP development: what to build first.
Does Brillnex Systems build AI SaaS products for other companies?
Yes. We design and build AI products and SaaS platforms for startups and growing businesses, in your repository and your stack. ConGreeto is our own product, and what we learn running it goes into the work we do for clients.
Sources
- Brillnex Systems, internal incident notes on ConGreeto, August–September 2026: website crawl on uncapped plans (September 2026; four attempts, zero crawl jobs, read directly from the database), onboarding content step (September 2026), lead scoring instruction (September 2026), banner paid-feature check (September 2026; every stored banner under the unchecked field name at the time of measurement), end-of-chat email delivery (August 2026). Internal, not a benchmark. Fixes 1, 3, 4 and 5 have regression tests.
From us
- SaaS development: if you're building an AI product and want it built by people who run one, tell us what you're building.
- The ConGreeto case study: the problem it was built to solve
- Multi-tenant AI SaaS architecture
- 12 questions to ask an AI development company before you sign
