The Error Message
Youβre running a query, and instead of data, your console throws this frustration:
MongoServerError: Projection cannot have a mix of inclusion and exclusion
This happens because MongoDB requires a clear strategy for filtering fields. You have to tell the database exactly what you want or exactly what you don't want. It won't let you do both at once.
Why This Error Occurs
Projection is how you shape your results. In a find() query, the second argument is the projection object. MongoDB enforces a strict logic: you must choose between a whitelist or a blacklist.
- Inclusion (Whitelist): You list the fields you need with a
1. MongoDB ignores everything else. Example:{ username: 1, email: 1 }. - Exclusion (Blacklist): You list the fields you want to hide with a
0. MongoDB returns everything else. Example:{ password: 0, ssn: 0 }.
The conflict arises when you try to mix these, like { username: 1, password: 0 }. MongoDB stops the query because it doesn't know whether to treat the unlisted fields as included or excluded.
The Only Exception: The _id Field
The _id field is the outlier. Because MongoDB includes _id by default, you are allowed to explicitly exclude it while whitelisting other fields. A projection like { email: 1, _id: 0 } is perfectly valid and very common in API development.
How to Fix the Query
Step 1: Spot the Conflict
Check your query code. In a typical Node.js or Mongoose environment, the error usually hides in a line like this:
// β This breaks because it mixes 1 and 0
const user = await db.collection('users').find({}, {
projection: { name: 1, hashed_password: 0 }
});
Step 2: Pick One Strategy
Decide which approach makes more sense for your specific use case. Usually, whitelisting is the better choice for security.
Option A: Stick to Inclusion
If you only need three fields out of a document with fifty fields, just list those three. It's cleaner and faster.
// β
Correct: Only returns name and email
db.collection('users').find({}, {
projection: { name: 1, email: 1, _id: 0 }
});
Option B: Stick to Exclusion
Use this when you want most of the data but need to hide sensitive internal fields like tokens or salt values.
// β
Correct: Returns everything EXCEPT the sensitive fields
db.collection('users').find({}, {
projection: { password: 0, internal_notes: 0, __v: 0 }
});
Aggregations and the $project Stage
The same logic applies to your aggregation pipelines. If your $project stage mixes 1s and 0s, the entire pipeline will fail. Here is how to fix a broken aggregation:
// β Broken Aggregation
{ $project: { total_price: 1, 'customer.address': 0 } }
// β
Fixed (Inclusion only)
{ $project: { total_price: 1, order_date: 1, items: 1 } }
Practical Tips for Production
- Security First: Always prefer inclusion (whitelisting). If you add a new sensitive field to your schema later, an exclusion-based query will accidentally leak that new field until you manually update the blacklist.
- Performance Boost: Projections aren't just about clean data. If your projection only includes fields that are part of an index, MongoDB can return results without even reading the full document from the disk. This is called a "Covered Query."
- Mongoose Shortcuts: If you use Mongoose, you can use string syntax like
.select('name email -_id'). Mongoose is smart enough to handle the 1s and 0s for you behind the scenes.
Quick Summary
To stop the error, look at your projection object. If you see a 1 and a 0 in the same block, remove one of them. Keep the 1s to define exactly what you want, or keep the 0s to define what to hide. Just remember that _id: 0 is your only free pass.

