Firebase Permission Denied: Fix Missing or Insufficient Permissions
Frustrating, but nine times out of ten it's your Firestore rules blocking reads. Here's the fast fix and why it works.
You're trying to read or write data from Firestore, and you get that red error: Error: Permission denied. It's annoying, especially when you're just testing. Let's fix it.
The Fix: Change Your Firestore Security Rules
Open the Firebase Console → Firestore Database → Rules tab. If you see something like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
That's your problem. The rule if false blocks everything. Change it to if true for development:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Click Publish. Wait 30 seconds. Try your operation again. It should work. Do not leave this in production — anyone on the internet can read/write your data. This is only for local testing or a prototype that never goes live.
Why This Works (The Underlying Cause)
Firestore's security rules are enforced server-side. When your app makes a read or write request, Firestore checks the rules match for that document path. If the rule evaluates to true, the request goes through. If false, it returns a permission denied error.
The default rule for new projects is allow read, write: if false; — this is Firebase being safe. But when you're testing locally, you probably haven't set up authentication yet. Without auth, your requests don't carry a valid token, so you can't use request.auth checks. Setting the rule to if true bypasses all auth checks. It's not a hack — it's the intended way to do development.
Less Common Variations That Look Like This Error
Sometimes the rule looks right but still fails. Here are the gotchas:
1. Wrong Collection Name in the Rule
Your rule might target /users/{userId} but your app is reading from /posts. That's a different path. The rule's match pattern must cover the collection you're accessing. Example: if you're reading /posts/abc123, your rule needs match /posts/{postId} or a wildcard like match /{document=**}.
2. Using request.auth.uid Before Authentication
A common mistake: you write a rule like allow read: if request.auth.uid != null; but your app hasn't signed in yet. The token is missing, so request.auth is null, and the rule evaluates to false. Always check if you're actually authenticated before hitting Firestore.
3. Recursive Wildcard Mismatch
If you use match /{document=**} at the top level, it covers every document. But if you write separate rules for different subcollections, the top-level rule might be overridden. Firestore uses the first matching rule, so order matters. Keep it simple: one wildcard rule for development.
4. Firestore Emulator vs. Production
If you're using the Firestore emulator locally, the rules are read from the firebase.json file. The console rules don't apply. So you might publish if true to the console, but your local emulator still has if false in the JSON. Check the firebase.json file's firestore.rules path.
How to Prevent This in the Future
Stop chasing the error. Here's what I do:
- Start with
if trueduring development. I never use authentication rules until I have the auth flow working first. It removes one variable. - Test with the Firestore Rules Simulator. In the console, go to Rules → Simulator. You can simulate a read or write from a specific path and see which rule matches, and whether it returns true or false. This catches typos and logic errors instantly.
- Add authentication gradually. Once your app can fetch data with
if true, then enable Firebase Auth. Then change your rules toallow read, write: if request.auth != null;. Then add per-document checks likerequest.auth.uid == userId. - Write rule tests. I use the Firebase Emulator Suite to run unit tests against my rules. It's extra setup, but it catches edge cases before they hit production.
The permission denied error is almost always the rules, not your code. Double-check the path, the rule syntax, and the auth state. You'll fix it in 2 minutes.
Was this solution helpful?