Living report · updated 25 September 2026

What breaks when
AI apps go live.

We grouped 59 recent, verified reports from builders whose apps broke near or after launch. Most failures fall in a few places: sign-in, hand-written access rules, file storage and payment webhooks.

Verified cases
59last 12 months · +3 this week
Posts judged
91620% of everything collected
Posts collected
4,469from 11 public sources
Sign-in share
34%then payments at 32%
Verified cases per week

28 in the last 12 weeks · about 2 a week

Where it breaks

Share of the 59 recent verified cases

The patterns · swipe

Two in three go-live failures trace to sign-in or access rules

1 / 7
0115 cases

Sign-in and sessions that don't hold in production

Users can't finish signing in, or sign-up stalls. Reset or email links fail or never arrive. The browser shows a signed-in user as logged out, a paying customer loses access to their account, or a token refresh signs real users out mid-session.

Cause & fix
Why it happens
Sign-in has several parts, and different layers own them. An email has to arrive, and its link has to redirect to the right origin. A cookie needs the right domain, SameSite and Secure attributes. Refresh-token rotation can race across tabs or parallel requests, and the server has to write and read session storage. Each part works in local dev on one domain. It breaks on the production domain, in a second tab, or when a mail provider's link scanner opens a one-time link before the user does.
The fix
Test the full flow on the production domain with a fresh inbox and a second browser. Check cookie attributes and the redirect allow-list. Set up SPF and DKIM on the sending domain. Make token refresh single-flight so only one request rotates the token. Log every auth failure with a reason, not just a 401.
With Gemmein
Sign-in uses one-time 8-digit email codes only. There are no passwords, so there is no password database and no reset flow to break. There is no social sign-in either. Codes last 10 minutes, sessions last 30 days, and each person has one session. Codes are sent from your own verified domain. Until that domain is verified, they go out as "<App name> (via Gemmein)". Your own server can check a session from any host with verifySession(token). The trade-off: if you need passwords or social login, Gemmein is not the right tool.
0210 cases

Access rules that let one customer reach another's data

It passes every test with one account. Then someone finds they can read, change or delete other customers' rows through the public API. Anonymous visitors can write into paying customers' records, a read the client can call returns credentials, or a new account makes itself an admin.

Cause & fix
Why it happens
Once the database key ships to the browser, row-level security is the only boundary, and someone has to write it by hand. With RLS off on a table, anyone holding the public key has full access. A policy of `using (true)`, or one that only checks that someone is signed in, passes every single-user test. If roles come from metadata the user can edit at sign-up, or from claims the client sets, users can grant themselves admin. A security-definer function the client can call skips policies entirely.
The fix
Turn on RLS for every table in exposed schemas and deny by default. Write separate select, insert, update and delete policies tied to ownership (`auth.uid()`), and add `with check` on writes. Keep roles in a table users cannot write to, or in server-only metadata. Then test with two real accounts and an anonymous client: user B must fail to read, change or delete user A's rows.
With Gemmein
There are no policies to write. Each collection gets exactly one of seven fixed rules written in plain English (private, shared, public_read, community, addressed, direct, admin_write), and Gemmein's servers enforce it. The app never implements authorization. Apps have no roles. Every session reports the role member, including the owner's, so nothing a user sends at sign-up can make them an admin. Tenant isolation is on by default, and a secret key refuses to run in a browser. With no passwords, there are no password hashes to leak.
039 cases

Access rules that lock out the rightful user, or that nobody can verify

Sign-up fails with a permission error. Signed-in users get empty results for their own data, or shared screens show the wrong records. Builders close to launch are stuck on their access rules and can't tell whether the rules are right.

Cause & fix
Why it happens
RLS denies by default, and a policy is missing or doesn't match. An insert policy may exist with no select policy, so the read after the insert fails. A profile row may be inserted during sign-up before a session exists, so `auth.uid()` is null. A policy may compare the owner against the wrong column or id type. Claims set on the server never reach the rule that checks them. Nothing in the stack says whether the rules are correct, and most apps are only tested by one developer signed in as themselves.
The fix
Create per-user rows from a server-side trigger or backend call, not from the client during sign-up. Write policies per operation, and read the real error instead of trusting an empty array. Add a two-account test to CI that checks both what must succeed and what must fail, and run it before every deploy.
With Gemmein
You pick each collection's rule from the fixed seven; you don't write it. Gemmein's MCP server (npx -y @gemmein/mcp) includes check_integration, which runs live isolation checks against the dev environment. It targets the bug that passes with one user and breaks with two. Reaffirm runs live calls in CI to check the app's boundaries. The limits are on purpose: no custom roles, no per-user record permissions, no team workspaces.
049 cases

Payment webhooks that never grant access

The customer pays and the provider shows the charge, but the app still shows the paywall. Live checkout completes and nothing happens, paid invoices never reach the app, or the webhook route returns 503.

Cause & fix
Why it happens
Access depends on a webhook that the app has to receive, verify and act on. The signature check fails when the framework parses the body before verification (it needs the raw bytes), or when the test-mode signing secret is still set in live. The live endpoint may never have been registered or subscribed to checkout.session.completed. Missing production env vars make the handler fail. Retries arrive twice or out of order, and the handler isn't idempotent.
The fix
Verify against the raw request body using the live endpoint's own secret. Register the live endpoint and subscribe it to the events you use. Make the handler idempotent by event id and return 2xx quickly. Reconcile against the provider's subscription state on sign-in or on a schedule, so one lost event doesn't cost a customer their access.
With Gemmein
Subscriptions and one-off digital products go through your own Stripe via Payment Links, so you never build a webhook. Other providers that sign their webhooks come in through a relay: a single JSON file that runs verbs such as grant_access and grant_plan. App Store and Play purchases come in through RevenueCat. Payments land in full in your own provider account. When access is missing, the app gets a typed entitlement_required error with the plan key, so it can show an upgrade prompt instead of a dead end.
056 cases

The whole auth and data backend goes down at once

Production sign-in, data reads and file loads all fail at the same moment, and every customer is locked out. On hosted app builders, there may be nobody to escalate to.

Cause & fix
Why it happens
Auth, database and storage sit in one project on one dependency path. An exhausted quota, a paused project, a rotated or expired key, an env var lost in a deploy, or an upstream incident takes all of them down together. Small apps rarely have health checks or alerts, so customers report the outage first.
The fix
Add an external uptime check that covers sign-in and one data read. Keep keys and env config in one place and check them after every deploy. Learn your provider's plan limits and pause rules. Show a clear error state in the app instead of a blank screen.
065 cases

File storage behind hand-written policies

Either every upload fails for every user, or anyone can open other customers' files without signing in. Builders who ask for owner-only uploads can't get them working.

Cause & fix
Why it happens
Storage has its own policy layer, separate from table rules and keyed on bucket and object path. A public bucket, or a policy that doesn't tie the path prefix to the user's id, exposes every file. A missing insert policy, or a path convention that differs between client and policy, blocks every upload. Once a public URL is shared, it bypasses the rules entirely.
The fix
Keep buckets private. Store objects under a path that starts with the owner's id, and write insert, select and delete policies that compare that folder to the signed-in user. Serve files through short-lived signed URLs. Test upload and download as two different users and as an anonymous visitor.
With Gemmein
Files belong to a person. AI job outputs are saved as sealed files on that person. The mobile SDKs support sealed files alongside private records. account.delete() removes a person's files along with the rest of their data.
075 cases

Subscription and pricing logic rebuilt by hand

A subscription charge is never created, an upgrade charges the wrong amount, or the app and the payment provider disagree about who is subscribed. Some apps trust a price sent from the client.

Cause & fix
Why it happens
A subscription is a state machine (trial, active, past due, upgrade, proration, cancel) split between the provider and the app's own table. Hand-rolled code copies that state instead of deriving it, computes proration itself, or accepts an amount or price id from the browser.
The fix
Make the provider the source of truth for subscription state, and keep only a cache keyed by customer id. Never accept a price or amount from the client; map a plan key to a price on the server. Let the provider compute proration, and preview the upgrade invoice before confirming.
With Gemmein
Plans and products are sold through your own Stripe Payment Links. The console has a subscriptions room, and each person's page shows their live grants and ledger. For AI features the price lives on the server: the app sends a tool name and inputs, never a price. The server spends or reserves credits before it calls the provider, and nothing spends from the browser. Gemmein deliberately does not do usage-based invoicing or seats.
This week's signals

What builders ran into.

Method

How every case is triaged.

4,469Collected

Public posts from the sources below, fetched politely, newest first.

994Scored

Rules flag posts that read like someone stuck going live.

916Judged

Claude judges each against one fixed definition, quoting the post.

208Verified

Kept only if that quote is found verbatim in the stored post.

59Last 12 months

The window every number on this page uses.

Patterns

Grouped into failure patterns; only aggregates are published.

Sources
1,257 github906 stackoverflow892 hackernews468 discord327 cursor forum183 github discussions150 devto100 reddit63 netlify forum62 vercel forum61 bubble forum

We collect public posts where builders describe a problem taking an app to production: Stack Overflow, GitHub issues, Hacker News, vendor and community forums, and public Discord support channels. Each candidate is judged against a fixed definition of "stuck going live" and kept only when the judgement is backed by a verbatim quote that is checked against the stored post. Patterns are grouped from those verified cases; counts can overlap. We never publish names, handles or quotes. Numbers update daily; the sample size is always shown.