This Firebase Firestore error occurs when setDoc() or updateDoc() receives invalid data in the document parameter. The document must be a plain JavaScript object. Fix it by ensuring you're passing a valid plain object and not a DocumentReference, class instance, or malformed data.
The "INVALID_ARGUMENT: Value for argument 'document' is invalid" error in Firebase Firestore indicates that the data you're trying to write doesn't meet Firestore's validation requirements. When you call setDoc() or updateDoc(), the second parameter must be a plain JavaScript object containing the fields you want to set. Firestore validates that the document data: - Is a plain object (not a class instance, DocumentReference, or circular reference) - Contains only valid Firestore data types (strings, numbers, booleans, dates, arrays, maps, etc.) - Doesn't exceed size limits (document size must be under 1 MiB) - Has valid field names (no undefined values in root object) When validation fails, Firestore throws this INVALID_ARGUMENT error to prevent corrupting your database with malformed data.
The most common cause is accidentally passing a DocumentReference instead of data:
WRONG - passing DocumentReference directly:
const docRef = doc(db, 'users', 'user123');
await setDoc(docRef); // ERROR: document is missing data
CORRECT - pass DocumentReference as first parameter, data as second:
const docRef = doc(db, 'users', 'user123');
const userData = { name: 'John', email: '[email protected]' };
await setDoc(docRef, userData);
WRONG - DocumentReference in data object:
const userRef = doc(db, 'users', 'user123');
const postData = {
title: 'My Post',
author: userRef // ERROR: DocumentReference not allowed here
};
await setDoc(doc(db, 'posts', 'post1'), postData);
CORRECT - use documentId field instead:
const postData = {
title: 'My Post',
authorId: 'user123' // Store just the ID
};
await setDoc(doc(db, 'posts', 'post1'), postData);
Or to store a reference for cross-referencing:
const postData = {
title: 'My Post',
author: 'user123' // Store as string ID
};
await setDoc(doc(db, 'posts', 'post1'), postData);
If using a class, convert to plain object before sending to Firestore:
WRONG - passing class instance:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return Hello ${this.name};
}
}
const user = new User('John', '[email protected]');
await setDoc(doc(db, 'users', 'user1'), user); // ERROR: class instance not allowed
CORRECT - convert to plain object:
// Method 1: Use object spread
const userPlain = {
name: user.name,
email: user.email
};
await setDoc(doc(db, 'users', 'user1'), userPlain);
// Method 2: Use JSON serialization
const userPlain = JSON.parse(JSON.stringify(user));
await setDoc(doc(db, 'users', 'user1'), userPlain);
// Method 3: Create plain object directly
const userData = {
name: 'John',
email: '[email protected]'
};
await setDoc(doc(db, 'users', 'user1'), userData);
// With TypeScript and interfaces:
interface IUser {
name: string;
email: string;
}
const userData: IUser = {
name: 'John',
email: '[email protected]'
};
await setDoc(doc(db, 'users', 'user1'), userData);
Firestore doesn't allow undefined values in the root object:
WRONG - undefined value in root:
const userData = {
name: 'John',
middleName: undefined, // ERROR: undefined not allowed
email: '[email protected]'
};
await setDoc(doc(db, 'users', 'user1'), userData);
CORRECT - remove undefined values:
// Method 1: Use Object.fromEntries with filter
const userData = {
name: 'John',
middleName: undefined,
email: '[email protected]'
};
const cleanData = Object.fromEntries(
Object.entries(userData).filter(([_, v]) => v !== undefined)
);
await setDoc(doc(db, 'users', 'user1'), cleanData);
// Method 2: Explicit fields only
const userData = {
name: 'John',
email: '[email protected]'
// middleName omitted entirely
};
await setDoc(doc(db, 'users', 'user1'), userData);
// Method 3: Helper function for form data
function cleanFormData(data) {
return Object.fromEntries(
Object.entries(data)
.filter(([_, value]) => value !== undefined && value !== null && value !== '')
);
}
const formData = {
name: 'John',
middleName: '',
email: '[email protected]'
};
await setDoc(doc(db, 'users', 'user1'), cleanFormData(formData));
If copying data from another document, use .data() method:
WRONG - passing entire snapshot:
const sourceDoc = await getDoc(doc(db, 'users', 'source'));
await setDoc(doc(db, 'users', 'copy'), sourceDoc); // ERROR: snapshot is not plain object
CORRECT - extract data from snapshot:
const sourceDoc = await getDoc(doc(db, 'users', 'source'));
if (sourceDoc.exists()) {
await setDoc(doc(db, 'users', 'copy'), sourceDoc.data());
}
// Or with type checking:
const sourceDoc = await getDoc(doc(db, 'users', 'source'));
const data = sourceDoc.data();
if (data) {
await setDoc(doc(db, 'users', 'copy'), data);
}
// Merging with existing document:
const sourceData = (await getDoc(doc(db, 'users', 'source'))).data() || {};
await setDoc(
doc(db, 'users', 'copy'),
sourceData,
{ merge: true } // Merge with existing fields
);
If document is very large, it may exceed Firestore's 1 MiB limit:
// Check document size before writing:
function getDocumentSize(doc) {
return new Blob([JSON.stringify(doc)]).size; // Size in bytes
}
const userData = {
name: 'John',
largeData: '...' // Large string
};
const sizeInBytes = getDocumentSize(userData);
const sizeInMiB = sizeInBytes / (1024 * 1024);
console.log(Document size: ${sizeInMiB.toFixed(2)} MiB);
if (sizeInMiB > 1) {
console.error('Document exceeds 1 MiB limit');
// Solution: Split into multiple documents or use Cloud Storage
}
// Split large data into separate documents:
const mainData = {
name: 'John',
email: '[email protected]'
};
const largeData = {
content: '...' // Large content
};
// Write to separate docs
await setDoc(doc(db, 'users', 'user1'), mainData);
await setDoc(doc(db, 'user_data', 'user1_content'), largeData);
// For very large files (videos, large documents):
// Use Cloud Storage instead
import { ref, uploadBytes } from 'firebase/storage';
const largeFile = new File(['...'], 'largefile.bin');
const storageRef = ref(storage, 'uploads/user1/largefile');
await uploadBytes(storageRef, largeFile);
// Reference the storage file from Firestore:
const userData = {
name: 'John',
fileUrl: 'gs://bucket/uploads/user1/largefile'
};
await setDoc(doc(db, 'users', 'user1'), userData);
Ensure all values can be serialized to JSON:
WRONG - using non-serializable types:
const userData = {
name: 'John',
callback: () => console.log('test'), // ERROR: Function not allowed
regex: /test/g, // ERROR: RegExp not allowed
symbol: Symbol('test'), // ERROR: Symbol not allowed
promise: Promise.resolve() // ERROR: Promise not allowed
};
await setDoc(doc(db, 'users', 'user1'), userData);
CORRECT - use serializable values:
// For Dates - Firestore has native Date support:
const userData = {
name: 'John',
createdAt: new Date() // Converted to Timestamp automatically
};
await setDoc(doc(db, 'users', 'user1'), userData);
// For custom objects, convert to JSON:
const userData = {
name: 'John',
config: JSON.stringify(complexObject) // Store as string
};
await setDoc(doc(db, 'users', 'user1'), userData);
// Using Firestore Timestamp for dates:
import { Timestamp } from 'firebase/firestore';
const userData = {
name: 'John',
createdAt: Timestamp.now()
};
await setDoc(doc(db, 'users', 'user1'), userData);
// For arrays with mixed types, ensure all elements are serializable:
WRONG:
const data = {
items: [1, 'text', { valid: true }, () => {}] // Function not allowed
};
CORRECT:
const data = {
items: [
{ type: 'number', value: 1 },
{ type: 'text', value: 'text' },
{ type: 'object', value: { valid: true } }
]
};
### DocumentReference Cross-References
If you need to store references between documents, there are several approaches:
1. Store IDs (recommended for most cases):
// Store just the ID
const postData = {
title: 'My Post',
authorId: userId // String ID
};
await setDoc(doc(db, 'posts', 'post1'), postData);
// Query by author:
const posts = await getDocs(
query(collection(db, 'posts'), where('authorId', '==', userId))
);2. Use subcollections for hierarchical relationships:
// Store comment under post's subcollection
const commentData = { text: 'Great post!', authorId: userId };
await setDoc(
doc(db, 'posts', 'post1', 'comments', 'comment1'),
commentData
);3. Use map fields for embedded data:
const postData = {
title: 'My Post',
author: {
id: userId,
name: 'John'
}
};
await setDoc(doc(db, 'posts', 'post1'), postData);### SDK Version Compatibility
This error can occur when using Firestore types from different SDK versions. Ensure consistency:
- Don't mix @firebase/firestore with older firebase packages
- Update all Firebase packages together
- For React Native, use @react-native-firebase/firestore
- For Flutter, use cloud_firestore package from the official Firebase team
### Type Safety with TypeScript
Define interfaces to catch type errors at compile time:
interface UserData {
name: string;
email: string;
createdAt: Date;
metadata?: Record<string, string>;
}
const userData: UserData = {
name: 'John',
email: '[email protected]',
createdAt: new Date()
};
await setDoc(doc(db, 'users', 'user1'), userData);messaging/UNSPECIFIED_ERROR: No additional information available
How to fix "messaging/UNSPECIFIED_ERROR: No additional information available" in Firebase Cloud Messaging
App Check: reCAPTCHA Score Too Low
App Check reCAPTCHA Score Too Low
storage/invalid-url: Invalid URL format for Cloud Storage reference
How to fix invalid URL format in Firebase Cloud Storage
auth/missing-uid: User ID identifier required
How to fix "auth/missing-uid: User ID identifier required" in Firebase
auth/invalid-argument: Invalid parameter passed to method
How to fix "auth/invalid-argument: Invalid parameter passed to method" in Firebase