Skip to content
Prodjex

Works in Preview, Breaks in Production: 7 Common Causes in AI-Built Apps

6 min read

Works in Preview, Breaks in Production: 7 Common Causes in AI-Built Apps

When an app works on your computer or in the Lovable, Bolt or Replit preview but breaks once it's deployed, the cause is usually the environment around the code, not the code itself. The most common culprits are missing environment variables, login redirects still pointing at the preview address, a production database that doesn't match the one you tested on, and hosting limits the preview never hit. You can find most of them in under an hour by reading your production logs and comparing settings side by side.

Here are the seven causes we see most, how to spot each one, and how to fix it.

Why "it worked in preview" is so common with AI-built apps

A preview environment is forgiving. It runs on one machine (or the AI tool's sandbox), with the tool's own settings, test data and keys already in place, and usually in development mode, which is more relaxed than a production build. When you deploy, the app moves to a different server, a different address, often a different database, and a stricter build process. Anything the preview quietly took care of now has to be set up on purpose.

AI coding tools are good at making the preview work. They're less consistent about the deployment details, because those live in dashboards and settings, not in the code they write.

1. Missing or different environment variables

Symptoms: blank pages, "undefined" errors, API calls failing with 401 or 403, features that work for you but not on the live site.

Why it happens: API keys, database URLs and other settings live in environment variables. Locally they're in a .env file. On your hosting platform, someone has to add each one by hand, and it's easy to miss one or paste the wrong value (a test key instead of a live one, or the development database URL).

There's also a timing trap. In Next.js, variables that start with NEXT_PUBLIC_ are inlined into the JavaScript bundle at build time, and the docs note that after the build, the app "will no longer respond to changes to these environment variables" (Next.js). Changing one in your hosting dashboard does nothing until you redeploy.

How to fix it: list every variable your app reads, compare that list with what's set in production, and redeploy after any change. Keep secrets out of NEXT_PUBLIC_ variables, since those are visible to anyone in the browser.

2. Login redirects that still point to the preview

Symptoms: login, sign-up confirmation emails, magic links or "Sign in with Google" send users to localhost or the preview address, or fail with a redirect error.

Why it happens: authentication providers only send users back to addresses you've approved. In Supabase, the Site URL is the default redirect, and the docs tell you to change it "from http://localhost:3000 to your production URL," and to add your real addresses to the Redirect URLs list (Supabase). Google, Microsoft and other sign-in providers have their own lists of allowed callback URLs too.

How to fix it: set the Site URL to your live domain, add every real domain (including www and any staging address) to the redirect list, and update the callback URLs in each sign-in provider's settings.

3. Domains, CORS and cookies

Symptoms: requests blocked with "CORS" errors in the browser console, users logged out on every page load, or login working on one domain but not another.

Why it happens: APIs often only accept requests from approved domains, and the list may only include localhost. Cookies behave differently on HTTPS, across subdomains, and between www and the bare domain. Something that worked on one address can fail on another.

How to fix it: add your production domains to every allowed-origins list (your API, Supabase, storage buckets and third-party services), pick one canonical domain and redirect the others to it, and check cookie settings for your live domain.

4. A production database that doesn't match

Symptoms: "relation does not exist" or "column not found" errors, empty pages where data should be, or users seeing permission errors.

Why it happens: the preview may have used a different database, or tables and columns were changed by hand in development and never recreated in production. Seed data and test accounts don't exist on the live site. And if you turned on row-level security in production (as you should), access rules that were never tested start blocking real requests.

How to fix it: manage database changes as migrations that run the same way in every environment, compare the two schemas, and test with a brand-new user account rather than the one you built the app with. Our guide to Supabase Row Level Security covers how to write rules that protect data without breaking the app.

5. Hosting limits the preview never hit

Symptoms: uploads or generated files disappear, long tasks fail with timeouts (on Vercel, a 504 FUNCTION_INVOCATION_TIMEOUT), or features fail only under real usage.

Why it happens: serverless platforms work differently from your laptop. On Vercel, functions run with a "read-only filesystem with writable /tmp scratch space" (Vercel), so you can't rely on saving files to the server. Each function also has a maximum run time, 300 seconds by default (Vercel). A report that took two minutes on your machine with sample data might take much longer with real data.

How to fix it: store files in proper storage (Supabase Storage, S3 or similar), move long-running work like large imports, AI processing or bulk emails into background jobs, and test with realistic amounts of data.

6. Development mode vs. a production build

Symptoms: the build fails on deploy, pages show old data, or something works in npm run dev but not in the live app.

Why it happens: development mode skips steps a production build enforces. Type errors and lint errors can stop the build. File name case matters on Linux servers (Header.tsx and header.tsx are different files), even though it didn't on your Mac or Windows machine. And frameworks like Next.js may pre-render or cache pages in production, so data that updated instantly in development looks stuck.

How to fix it: run a production build locally before every deploy (npm run build, then npm start), fix every warning the build reports, and check how each page is meant to fetch and refresh its data.

7. Third-party services still in test mode

Symptoms: payments succeed but orders never update, emails don't arrive, or an external service rejects requests.

Why it happens: Stripe test keys and webhooks were set up for the preview address, not production. Email services often limit sending until you verify your domain. Some APIs have tighter rate limits or require approval before you can go live.

How to fix it: switch every service to live keys, create production webhooks pointing at your real domain (and verify their signatures), verify your sending domain for email, and send a real test transaction end to end.

How to find the cause fast

  1. Read the production logs first. Your hosting platform's function logs and the browser's developer console (Console and Network tabs) usually point straight at the problem.
  2. Reproduce it with a production build locally. If it fails locally too, it's the build or the code. If it only fails when deployed, it's the environment.
  3. Compare the two environments side by side: environment variables, database schema, auth settings, domains and third-party keys.
  4. Change one thing at a time and redeploy, so you know what fixed it.
  5. Be careful with AI-suggested fixes. Asking the AI tool to "fix the production error" without the logs often produces a change that hides the symptom, like disabling a security rule, instead of fixing the cause.

How to stop it happening again

  • Keep a staging environment that matches production, and deploy there first.
  • Write a deployment checklist: environment variables, auth redirects, domains, database migrations, live keys, webhooks and email domain.
  • Use migrations for every database change.
  • Add error monitoring and alerts, so you hear about failures before your customers do.
  • Review security before launch. Many of the settings above are also security settings. Our posts on vibe-coded app security and the Lovable and Bolt production checklist cover what to check.

When to get help

If you've worked through the list and the app still breaks, or every fix seems to cause a new problem, a second set of eyes usually saves days. Our AI-built app rescue service reviews your code, infrastructure and data access, fixes what's broken, and handles the last steps of launch: deployment, domains, payments and app stores. We work with Next.js, React, Supabase, Firebase and the code that Lovable, Bolt, Cursor and similar tools produce, and once you're live our care plans keep it running.

Tell us what's breaking and what you built it with. We'll tell you what we'd check first.

  • ai generated code
  • deployment
  • lovable
  • supabase
  • vercel
  • debugging

Keep reading