# How to Fix "Firestore: permission-denied" in Firebase

- Tool: Firebase
- Difficulty: Intermediate
- Fix time: 10-30 minutes
- Compatibility: Firebase Firestore — Web, Android, iOS, Flutter, Admin SDK
- Last updated: March 2026

## TL;DR

The 'Firestore: permission-denied' error means your Firestore security rules are blocking the operation your code is attempting. This is the SDK-level error code for the same issue as 'Missing or insufficient permissions.' Use the Firebase Rules Playground to test your rules, ensure queries structurally match rule conditions, and verify user authentication state.

## What does "Firestore: permission-denied" mean?

When Firestore returns the permission-denied error code, your security rules evaluated the operation and determined it should be blocked. On web this appears as [code=permission-denied], on Android as FirebaseFirestoreException: PERMISSION_DENIED, and on Flutter as [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.

This error is distinct from the more generic 'Missing or insufficient permissions' message in that it specifically comes from the Firestore SDK with a structured error code. The underlying cause is always the same: your security rules deny the operation. However, this version often provides slightly more context about which operation was denied.

The most important concept for understanding this error: Firestore rules are not filters. Firestore evaluates whether a query could theoretically return unauthorized data. If your rules restrict by userId, any query without a matching .where('userId', '==', uid) clause is rejected — even if every document would pass the rule check individually. CollectionGroup queries require special rules matching the subcollection path.

## Common causes

- **The user is not authenticated** — and the security rules require request.auth != null for the operation
- **A read query does not** — include a where clause that structurally matches the security rule conditions
- **A write operation sends data that** — fails rule-based validation (e.g., missing required fields or incorrect data types)
- **A collectionGroup query is missing** — corresponding rules for the subcollection path using match /{path=**}/subcollection/{doc}
- **The user's authentication token has** — expired and the app has not refreshed it before making the Firestore call
- **Custom claims used in security** — rules were recently updated but the user's token has not been refreshed to include the new claims

## How to fix "Firestore: permission-denied"

Open the Firebase Console, go to Firestore > Rules > Rules Playground. Simulate the exact operation your code performs — specify the collection path, operation type (get, list, create, update, delete), and the auth context. The playground shows exactly which rule matched and why it denied the request.

For read operations, make sure your query includes where clauses that match your rule conditions. If your rule says allow read: if request.auth.uid == resource.data.ownerId, your query must include .where('ownerId', '==', currentUser.uid).

For write operations, verify that the data you are sending matches the rule's validation. Rules can check request.resource.data fields for required values, types, and constraints. A missing or incorrectly typed field will cause a permission denial.

For authentication issues, use onAuthStateChanged to wait for auth to be fully initialized before making Firestore calls. Queries made during the brief moment after page load but before auth state resolves will be treated as unauthenticated. If you use custom claims, call user.getIdToken(true) to force a token refresh after updating claims server-side.

Before:

```
// Rules require auth but query doesn't wait for auth state
const db = getFirestore();
// This runs before auth resolves!
const snapshot = await getDocs(collection(db, 'userPosts'));
```

After:

```
import { getAuth, onAuthStateChanged } from 'firebase/auth';
import { getFirestore, collection, query, where, getDocs } from 'firebase/firestore';

const auth = getAuth();
const db = getFirestore();

// Wait for auth to resolve before querying
onAuthStateChanged(auth, async (user) => {
  if (user) {
    // Query matches rule: allow read if request.auth.uid == resource.data.ownerId
    const q = query(
      collection(db, 'userPosts'),
      where('ownerId', '==', user.uid)
    );
    const snapshot = await getDocs(q);
    snapshot.forEach(doc => console.log(doc.data()));
  } else {
    console.log('User not signed in');
  }
});
```

## Tips to prevent this

- Always test rules in the Firebase Rules Playground before deploying — it shows exactly why a request is allowed or denied with the specific rule that matched
- Use onAuthStateChanged to ensure authentication is fully initialized before making any Firestore calls, preventing queries from running as unauthenticated
- For collectionGroup queries, add a specific rule at match /{path=**}/subcollection/{doc} — collection-level rules do not automatically apply to group queries
- After updating custom claims on the server, call user.getIdToken(true) on the client to force a token refresh so the new claims take effect immediately

## Frequently asked questions

### What is the difference between "Firestore: permission-denied" and "Missing or insufficient permissions"?

They are the same error at different levels. 'permission-denied' is the specific SDK error code, while 'Missing or insufficient permissions' is the human-readable message. Both mean your Firestore security rules blocked the operation. The cause and fix are identical.

### Why does my Firestore query fail with permission-denied even when all documents match my rules?

Firestore rules are not filters. Firestore evaluates whether the query could theoretically access unauthorized documents, not whether the actual results would pass. If your rules check a userId field, the query must include a matching .where() clause to structurally prove it only accesses authorized data.

### How do I debug Firestore permission-denied errors?

Use the Firebase Console Rules Playground (Firestore > Rules > Rules Playground). It lets you simulate any operation with specific auth context and shows exactly which rule allowed or denied the request. This is the single most reliable debugging tool.

### Can expired authentication tokens cause Firestore permission-denied?

Yes. Firebase Auth tokens expire after one hour. The SDK usually refreshes them automatically, but in some cases (offline usage, background tabs), the token may expire. Calling user.getIdToken(true) forces a refresh. Also ensure your code waits for onAuthStateChanged before making Firestore calls.

### Do AI code generators frequently cause this Firestore error?

Yes. AI tools like Lovable, Cursor, and V0 commonly generate Firestore queries without matching security rules, or they create permissive development rules that break when tightened for production. Always review both the generated queries and rules together.

---

Source: https://www.rapidevelopers.com/ai-build-errors/firestore-permission-denied
© RapidDev — https://www.rapidevelopers.com/ai-build-errors/firestore-permission-denied
