"Missing or insufficient permissions": the Firestore error that hits AI built apps after 30 days
You built an app with Bolt, v0, Cursor, or Replit Agent on top of Firebase. It worked fine, maybe for weeks. Then one day every screen is empty, nothing saves, and the browser console shows FirebaseError: Missing or insufficient permissions with code permission-denied. Your data is still there and your code has not changed. What changed is that a security rule stopped saying yes. Usually it is a rule with an expiry date baked in, and the date passed.
The symptom: the app just looks empty
Firestore permission errors rarely crash anything visible. The read fails, the app renders with no data, and the page looks like a design bug: empty lists, blank dashboards, spinners that never resolve. The real evidence is in devtools. Press F12, open the Console tab, and reload. If you see FirebaseError: Missing or insufficient permissions or any error with code permission-denied, stop debugging your frontend. The app looks broken but the block is on the backend, in your Firestore security rules. This is the same shape as the Supabase data not showing problem, just wearing Firebase clothes.
Cause 1: your test mode rules expired (the "worked for a month then broke" classic)
When a Firestore database is created in test mode, which is what most AI builders and most humans pick to get moving, Firebase writes rules like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.time < timestamp.date(2026, 10, 2);
}
}
}
Read that condition carefully. It allows everything only while the current time is before a hardcoded date, set 30 days out from creation. On day 31 every single read and write starts failing with permission-denied, project wide, at once. If your app worked perfectly and then died overnight roughly a month after you set it up, this is almost certainly it, and Firebase even emails a warning to the project owner beforehand, which lands in an inbox nobody checks.
To confirm: open the Firebase console, pick your project, go to Firestore Database, then the Rules tab. The Rules tab shows the exact rules currently live, with the publish date at the top. If you see request.time < timestamp.date(...) with a date in the past, you have found the bug. Do not fix it by bumping the date another month; that just schedules the next outage. Replace the rules with real ones, below.
Cause 2: rules require login, but the app reads before login exists
The other common rule shape is allow read, write: if request.auth != null;, which means "any signed in user". That rule fails in two ways AI builders trigger constantly:
- The app queries Firestore before sign-in completes. Auth is asynchronous. If the code fetches data on page load instead of waiting for
onAuthStateChangedto report a user, the first queries run withrequest.authequal to null and get denied. Symptom: errors on first load, sometimes fine after a refresh. Fix in code: only start Firestore reads after the auth listener fires with a user. - The code uses anonymous sign-in, but Anonymous auth was never enabled. AI tools love generating
signInAnonymously(auth), and it throws if the provider is off. Check the console under Authentication, then Sign-in method: whatever the code calls (Anonymous, Google, Email/Password) must be enabled there. If sign-in fails,request.authstays null and every rule requiring it denies.
Cause 3: the rules name collections your app no longer uses
Rules match on document paths. If the rules say match /posts/{postId} but a later AI iteration renamed the collection to articles, there is no matching rule for articles, and no matching rule means denied, that is the default. This happens a lot in vibe coded apps because the AI freely renames collections between prompts and never updates the rules. Compare the collection names in the Rules tab against the ones the code actually queries (search the repo for collection(). Every collection the app touches needs a matching rule block.
Cause 4: rules validate fields the app does not send
Stricter rules can check the contents of a write, for example allow create: if request.resource.data.ownerId == request.auth.uid;. If the app writes a document without setting ownerId, or sets a different field name like userId, the check fails and the write is denied even though the user is signed in and the path matches. When reads work but one specific save fails, read the rule for that collection and compare every field it mentions against the object the code sends.
Cause 5: it might be Storage rules, not Firestore rules
Firebase has two separate rule sets: Firestore rules for your database and Storage rules for uploaded files. Both throw permission errors with near identical wording. If the failure happens on an image or file upload or download, you are in Cloud Storage: in the console go to Storage, then its own Rules tab. Storage created in test mode has the same 30 day expiring timestamp and dies the same way. Fixing Firestore rules does nothing for Storage and vice versa, so check which product the failing call targets.
Reproduce it with the Rules Playground
The Rules tab has a built in simulator, the Rules Playground, in the left panel of the rules editor. Set the simulation type (get, list, create, update, delete), enter the exact document path your app uses, toggle Authenticated on or off to mirror your app's state, and run it. It tells you allowed or denied and highlights the exact rule line that made the decision. This is the fastest way to test a fix before publishing, and to prove which of the causes above you are hitting.
A safe starter rule set for an app with login
For the most common vibe coded shape, where each user owns their own documents, keyed by their user id:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Each user reads and writes only their own profile document
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Documents owned via an ownerId field
match /notes/{noteId} {
allow read, update, delete: if request.auth != null
&& resource.data.ownerId == request.auth.uid;
allow create: if request.auth != null
&& request.resource.data.ownerId == request.auth.uid;
}
}
}
Rename notes and ownerId to match your actual collections and fields, add a block per collection, make sure the code sets ownerId on every create, then test each path in the Playground and hit Publish. Publishing takes effect in about a minute, no redeploy of your app needed, because rules live in Firebase, not in your code.
Do not do this
The tempting one line fix is allow read, write: if true;. It makes every error vanish and it makes your entire database public: anyone on the internet with your Firebase config, which ships inside your frontend bundle by design, can read, overwrite, and delete everything. Bots scan for exactly this. The expiring test mode rule exists specifically so people cannot leave a database open forever, so replacing it with a permanently open rule is worse than the outage you started with. If an AI chat suggests it when you paste the error in, refuse. Real rules are ten lines, and you now have a template.
Still stuck?
Run the instant diagnosis. Paste your public repo URL into the form and an automated clean-room check reports what is broken: install, build, render, and config, with the exact blocker named, in minutes, free. Security rules live in your Firebase project rather than your code, so for this one also paste the console error and your current rules from the Rules tab into the issue and I will look at it the same day. Private repo or zip? Email works too.
Get an instant free diagnosisOr email me instead. Fixes with 24 hour turnaround start at $95. Prefer self-serve? The $5 instant diagnosis on Apify checks your repo privately, no public issue needed.