Automattic/mongoose · error · TypeError

Invalid select() argument. Must be string or object.

Error message

Invalid select() argument. Must be string or object.

What it means

Mongoose's Query.prototype.select() only accepts projections as a string (e.g. 'name -email') or a plain object (e.g. { name: 1, email: 0 }); falsy arguments (0, false, '') return early as a no-op. The argument is run through parseProjection() and only the object branch can produce a projection, so any remaining truthy non-object value falls through to this terminal TypeError. Passing more than one argument triggers a different, earlier error ('Invalid select: select only takes 1 argument'), so this one specifically means a single value of the wrong type.

Source

Thrown at lib/query.js:1203

              delete userProvidedFields[field];
            }
          });
        }
      });
    } else {
      const keys = Object.keys(arg);
      for (let i = 0; i < keys.length; ++i) {
        const value = arg[keys[i]];
        const key = keys[i];
        fields[key] = sanitizeValue(value);
        userProvidedFields[key] = sanitizeValue(value);
      }
    }

    return this;
  }

  throw new TypeError('Invalid select() argument. Must be string or object.');
};

/**
 * Enable or disable schema level projections for this query. Enabled by default.
 * Set to `false` to include fields with `select: false` in the query result by default.
 *
 * #### Example:
 *
 *     const userSchema = new Schema({
 *       email: { type: String, required: true },
 *       passwordHash: { type: String, select: false, required: true }
 *     });
 *     const UserModel = mongoose.model('User', userSchema);
 *
 *     const doc = await UserModel.findOne().orFail().schemaLevelProjections(false);
 *
 *     // Contains password hash, because `schemaLevelProjections()` overrides `select: false`
 *     doc.passwordHash;

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Pass a string projection like .select('name email') (prefix a field with - to exclude) or an object like .select({ name: 1, email: 0 })
  2. If the projection comes from user input, whitelist allowed field names and build the string/object yourself before calling select()
  3. To clear or no-op the projection, call .select() with no argument or a falsy value
  4. Check the variable you are passing is not accidentally a number, boolean, or function

Example fix

// before
const users = await User.find().select(1);   // TypeError: Invalid select() argument
const users2 = await User.find().select(true); // same TypeError

// after
const users = await User.find().select('name email');
// or
const users2 = await User.find().select({ name: 1, email: 1 });
Defensive patterns

Strategy: type-guard

Validate before calling

const ALLOWED_FIELDS = new Set(['name', 'email', 'age']);
function buildProjection(raw) {
  if (raw == null) return undefined;
  const fields = (typeof raw === 'string' ? raw.split(/\s+/) : Array.isArray(raw) ? raw : [])
    .map(f => f.replace(/^-/, ''))
    .filter(f => ALLOWED_FIELDS.has(f));
  return fields.length ? fields.join(' ') : undefined;
}
const docs = await Model.find().select(buildProjection(req.query.fields));

Type guard

function isSelectArg(arg) {
  return arg == null || typeof arg === 'string' || (typeof arg === 'object' && arg !== null);
}

Try / catch

try {
  query.select(projection);
} catch (err) {
  if (err instanceof TypeError && err.message.includes('select()')) {
    throw new Error(`Invalid projection passed to select(): ${JSON.stringify(projection)}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: .select(1), .select(true), or .select(() => buildFields()); passing an unvalidated request value (req.query.fields) that resolves to a number or boolean; passing a variable that was expected to be a projection object but is actually a primitive.

Common situations: Dynamically building projections from user input without validating shape; passing a boolean or numeric flag where a projection spec belongs; porting code from other ORMs where any truthy value is stringified into a field list.

Related errors


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/c24cb09567ed858c. Report an issue: GitHub.