Fixing MongoDB Error: Stage $project failed: The argument to $size must be an array

intermediate🍃 MongoDB2026-06-18| MongoDB 3.4+, Linux, macOS, Windows, MongoDB Atlas

Error Message

Stage $project failed: The argument to $size must be an array, but was of type: missing
#mongodb#aggregation#array#$size

TL;DR: The Quick Fix

This error hits because $size is picky. It requires an array, but it found null or a missing field instead. To fix it instantly, wrap your field with $ifNull to provide an empty array [] as a fallback.

// The broken way:
{ $project: { count: { $size: "$tags" } } }

// The safe way:
{ 
  $project: { 
    count: { 
      $size: { $ifNull: [ "$tags", [] ] } 
    } 
  } 
}

Why Your Pipeline is Crashing

MongoDB's flexibility is a double-edged sword. Since there is no strict schema, one document might have a tags array while the next one omits it entirely. When $size encounters that missing field, it doesn't just return zero—it kills the entire aggregation.

Even if you have 1,000,000 perfect documents, a single record with a missing field will cause the query to fail. This usually happens for three reasons:

  • Missing Fields: The key simply wasn't included in the document.
  • Null Values: The field exists but was explicitly set to null by an application or import script.
  • Data Type Mismatch: A field contains a string (like "none") or an object instead of the expected array.

Battle-Tested Solutions

Method 1: Use $ifNull (The Standard Fix)

If you mainly deal with missing or null fields, $ifNull is your best friend. It acts as a safety net. If the field is missing, it passes an empty array to $size, which then correctly returns a count of 0.

db.posts.aggregate([
  {
    $project: {
      title: 1,
      comment_count: {
        $size: { $ifNull: [ "$comments", [] ] }
      }
    }
  }
])

Method 2: Use $isArray (The Bulletproof Approach)

Production data is often messy. You might find a document where comments is a boolean or a string. $ifNull won't catch those types. For total safety, use $isArray to verify the data before measuring it.

db.posts.aggregate([
  {
    $addFields: {
      comment_count: {
        $cond: {
          if: { $isArray: "$comments" },
          then: { $size: "$comments" },
          else: 0
        }
      }
    }
  }
])

This conditional logic ensures your pipeline stays alive regardless of what data types are lurking in your collection.

Method 3: Filter Early with $match (The Performance Choice)

Sometimes you don't want to process "broken" documents at all. By filtering for arrays at the start of your pipeline, you reduce the workload for later stages. This is often faster because it leverages indexes.

db.posts.aggregate([
  {
    $match: {
      comments: { $exists: true, $type: "array" }
    }
  },
  {
    $project: {
      comment_count: { $size: "$comments" }
    }
  }
])

Verify Your Fix

Don't guess—test. Create a small collection with "dirty" data to confirm your aggregation handles every edge case.

db.test_collection.insertMany([
  { _id: 1, items: ["apple", "banana"] }, // Valid array
  { _id: 2 },                            // Missing field
  { _id: 3, items: null },               // Null field
  { _id: 4, items: "not-an-array" }      // String (use Method 2 for this!)
]);

// Run the verification
db.test_collection.aggregate([
  {
    $project: {
      count: { 
        $cond: [
          { $isArray: "$items" }, 
          { $size: "$items" }, 
          0
        ]
      }
    }
  }
]);

A successful fix will return counts of 2, 0, 0, and 0 without a single error message.

Further Reading

Related Error Notes