Mongoose is a comprehensive MongoDB object modeling tool designed for asynchronous environments with schema-based validation, query building, and business logic hooks.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive error system with specific error types for different failure scenarios, validation errors, and MongoDB operation errors.
Foundation error classes that other Mongoose errors extend from.
/**
* Base class for all Mongoose errors
*/
class MongooseError extends Error {
/** Error name identifier */
name: 'MongooseError';
/** Error message */
message: string;
/** Error stack trace */
stack?: string;
}
/**
* MongoDB driver errors wrapped by Mongoose
*/
class MongoError extends Error {
/** MongoDB error code */
code?: number;
/** Error code name */
codeName?: string;
/** Write errors for bulk operations */
writeErrors?: any[];
/** Result object for failed operations */
result?: any;
}Errors related to document validation and schema constraints.
/**
* Document validation failure error
*/
class ValidationError extends MongooseError {
name: 'ValidationError';
/** Validation errors by field path */
errors: { [path: string]: ValidatorError };
/** Add validation error for a path */
addError(path: string, error: ValidatorError): void;
}
/**
* Individual field validation failure
*/
class ValidatorError extends MongooseError {
name: 'ValidatorError';
/** Field path that failed validation */
path: string;
/** Value that failed validation */
value: any;
/** Validator type (required, min, max, etc.) */
kind: string;
/** Schema type that was validated */
schemaType?: SchemaType;
/** Validation properties */
properties?: any;
}Usage Examples:
const userSchema = new mongoose.Schema({
name: { type: String, required: true, maxlength: 50 },
email: { type: String, required: true, unique: true },
age: { type: Number, min: 0, max: 120 }
});
const User = mongoose.model('User', userSchema);
try {
const user = new User({
name: '', // Too short
age: -5 // Below minimum
// email missing (required)
});
await user.save();
} catch (error) {
if (error instanceof mongoose.Error.ValidationError) {
console.log('Validation failed:', error.message);
// Access individual field errors
Object.keys(error.errors).forEach(path => {
const fieldError = error.errors[path];
console.log(`${path}: ${fieldError.message}`);
console.log(`Kind: ${fieldError.kind}, Value: ${fieldError.value}`);
});
}
}
// Custom validation with error handling
userSchema.path('email').validate(async function(email) {
const count = await mongoose.model('User').countDocuments({ email });
return count === 0;
}, 'Email already exists');
// Validation with custom error message
userSchema.path('age').validate(function(age) {
return age >= 13;
}, 'Users must be at least 13 years old');Errors that occur when type casting fails during queries or document creation.
/**
* Type casting failure error
*/
class CastError extends MongooseError {
name: 'CastError';
/** Field path where casting failed */
path: string;
/** Value that failed to cast */
value: any;
/** Target type for casting */
kind: string;
/** Error reason if available */
reason?: Error;
/** Schema type that failed to cast */
schemaType?: SchemaType;
/** Model associated with the error */
model?: Model<any>;
}Usage Examples:
try {
// Invalid ObjectId casting
const user = await User.findById('invalid-object-id');
} catch (error) {
if (error instanceof mongoose.Error.CastError) {
console.log('Cast error:', error.message);
console.log('Path:', error.path); // '_id'
console.log('Value:', error.value); // 'invalid-object-id'
console.log('Kind:', error.kind); // 'ObjectId'
}
}
try {
// Invalid date casting
const user = new User({
name: 'John',
birthDate: 'not-a-date'
});
await user.save();
} catch (error) {
if (error instanceof mongoose.Error.CastError) {
console.log(`Failed to cast "${error.value}" to ${error.kind} for path "${error.path}"`);
}
}
// Handle cast errors in queries
try {
const users = await User.find({ age: 'not-a-number' });
} catch (error) {
if (error instanceof mongoose.Error.CastError) {
console.log('Query cast error:', error.message);
}
}Errors related to document operations and database interactions.
/**
* Document not found error for operations expecting a document
*/
class DocumentNotFoundError extends MongooseError {
name: 'DocumentNotFoundError';
/** Query filter that found no documents */
filter: any;
/** Query that failed */
query: Query<any, any>;
}
/**
* Version conflict error for optimistic concurrency control
*/
class VersionError extends MongooseError {
name: 'VersionError';
/** Document that had version conflict */
document: Document;
/** Expected version number */
version: number;
}
/**
* Parallel save operation error
*/
class ParallelSaveError extends MongooseError {
name: 'ParallelSaveError';
/** Document being saved in parallel */
document: Document;
}
/**
* Error when trying to remove a document that's already being removed
*/
class ParallelValidateError extends MongooseError {
name: 'ParallelValidateError';
}Usage Examples:
try {
// Document not found error
const user = await User.findOneAndUpdate(
{ email: 'nonexistent@example.com' },
{ name: 'Updated Name' },
{
new: true,
runValidators: true,
context: 'query',
// This option causes DocumentNotFoundError if no doc found
upsert: false,
strict: false,
overwrite: false
}
);
if (!user) {
throw new mongoose.Error.DocumentNotFoundError();
}
} catch (error) {
if (error instanceof mongoose.Error.DocumentNotFoundError) {
console.log('Document not found:', error.filter);
}
}
// Version error handling
const userSchema = new mongoose.Schema({
name: String,
version: { type: Number, default: 0 }
});
userSchema.pre('save', function() {
if (this.isModified() && !this.isNew) {
this.increment(); // Increment version
}
});
try {
const user = await User.findById(userId);
// Simulate concurrent modification
setTimeout(async () => {
await User.findByIdAndUpdate(userId, { name: 'Changed by someone else' });
}, 100);
user.name = 'My change';
await user.save(); // May throw VersionError
} catch (error) {
if (error instanceof mongoose.Error.VersionError) {
console.log('Version conflict - document was modified by another process');
}
}
// Parallel save error
const user = new User({ name: 'John' });
try {
// Don't await both saves - this can cause ParallelSaveError
Promise.all([user.save(), user.save()]);
} catch (error) {
if (error instanceof mongoose.Error.ParallelSaveError) {
console.log('Cannot save document multiple times in parallel');
}
}Errors related to schema definition and model registration.
/**
* Missing schema error
*/
class MissingSchemaError extends MongooseError {
name: 'MissingSchemaError';
}
/**
* Model overwrite error when trying to redefine existing model
*/
class OverwriteModelError extends MongooseError {
name: 'OverwriteModelError';
}
/**
* Schema type definition error
*/
class MongooseSchemaError extends MongooseError {
name: 'MongooseSchemaError';
}
/**
* Invalid schema option error
*/
class InvalidSchemaOptionError extends MongooseError {
name: 'InvalidSchemaOptionError';
/** Option name that was invalid */
option: string;
}Usage Examples:
try {
// Trying to create model without schema
const User = mongoose.model('User');
} catch (error) {
if (error instanceof mongoose.Error.MissingSchemaError) {
console.log('Schema is required to create a model');
}
}
try {
// Trying to redefine existing model
const User1 = mongoose.model('User', userSchema);
const User2 = mongoose.model('User', anotherSchema); // Error
} catch (error) {
if (error instanceof mongoose.Error.OverwriteModelError) {
console.log('Cannot overwrite model once registered');
// Use mongoose.deleteModel() first, or pass { overwriteModels: true }
}
}
// Schema definition errors
try {
const invalidSchema = new mongoose.Schema({
name: {
type: String,
invalidOption: true // This might cause schema error
}
});
} catch (error) {
if (error instanceof mongoose.Error.MongooseSchemaError) {
console.log('Schema definition error:', error.message);
}
}Errors related to database connections and server communication.
/**
* Server selection error when unable to connect to MongoDB
*/
class MongooseServerSelectionError extends MongooseError {
name: 'MongooseServerSelectionError';
/** Reason for server selection failure */
reason?: Error;
}
/**
* Connection timeout error
*/
class MongooseTimeoutError extends MongooseError {
name: 'MongooseTimeoutError';
}
/**
* Disconnected error when operation attempted on closed connection
*/
class DisconnectedError extends MongooseError {
name: 'DisconnectedError';
}
/**
* Strict mode violation error
*/
class StrictModeError extends MongooseError {
name: 'StrictModeError';
/** Path causing strict mode violation */
path: string;
/** Invalid value */
value: any;
}
/**
* Populate path doesn't exist in strict populate mode
*/
class StrictPopulateError extends MongooseError {
name: 'StrictPopulateError';
/** Population path */
path: string;
}
/**
* Bulk save operations incomplete error
*/
class MongooseBulkSaveIncompleteError extends MongooseError {
name: 'MongooseBulkSaveIncompleteError';
/** Failed operations */
failedOperations: any[];
}Usage Examples:
try {
await mongoose.connect('mongodb://invalid-host:27017/myapp', {
serverSelectionTimeoutMS: 5000
});
} catch (error) {
if (error instanceof mongoose.Error.MongooseServerSelectionError) {
console.log('Could not connect to MongoDB server');
console.log('Reason:', error.reason?.message);
}
}
// Handle connection errors
mongoose.connection.on('error', (error) => {
if (error instanceof mongoose.Error.MongooseTimeoutError) {
console.log('Database operation timed out');
} else if (error instanceof mongoose.Error.DisconnectedError) {
console.log('Database connection lost');
}
});
// Retry logic for connection errors
async function connectWithRetry() {
const maxRetries = 3;
let retries = 0;
while (retries < maxRetries) {
try {
await mongoose.connect(process.env.MONGODB_URI);
console.log('Connected to MongoDB');
break;
} catch (error) {
if (error instanceof mongoose.Error.MongooseServerSelectionError) {
retries++;
console.log(`Connection attempt ${retries} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000 * retries));
} else {
throw error; // Re-throw non-connection errors
}
}
}
}Common patterns for handling Mongoose errors in applications.
// Error handling middleware for Express.js
function handleMongooseError(error, req, res, next) {
if (error instanceof mongoose.Error.ValidationError) {
return res.status(400).json({
type: 'ValidationError',
message: 'Validation failed',
errors: Object.keys(error.errors).map(path => ({
path,
message: error.errors[path].message,
value: error.errors[path].value
}))
});
}
if (error instanceof mongoose.Error.CastError) {
return res.status(400).json({
type: 'CastError',
message: `Invalid ${error.kind}: ${error.value}`,
path: error.path
});
}
if (error instanceof mongoose.Error.DocumentNotFoundError) {
return res.status(404).json({
type: 'DocumentNotFoundError',
message: 'Document not found'
});
}
if (error.code === 11000) { // MongoDB duplicate key error
return res.status(409).json({
type: 'DuplicateKeyError',
message: 'Duplicate key error',
keyValue: error.keyValue
});
}
// Generic server error
res.status(500).json({
type: 'InternalServerError',
message: 'An unexpected error occurred'
});
}Usage Examples:
// Comprehensive error handling in async functions
async function createUser(userData) {
try {
const user = new User(userData);
await user.save();
return { success: true, user };
} catch (error) {
if (error instanceof mongoose.Error.ValidationError) {
return {
success: false,
type: 'validation',
errors: Object.keys(error.errors).map(path => ({
field: path,
message: error.errors[path].message,
value: error.errors[path].value
}))
};
}
if (error.code === 11000) { // Duplicate key (unique constraint)
return {
success: false,
type: 'duplicate',
message: 'Email already exists'
};
}
// Re-throw unexpected errors
throw error;
}
}
// Promise-based error handling
User.create(userData)
.then(user => {
console.log('User created:', user);
})
.catch(error => {
if (error instanceof mongoose.Error.ValidationError) {
console.log('Validation errors:', error.errors);
} else {
console.error('Unexpected error:', error);
}
});
// Using async/await with try-catch
async function updateUser(id, updateData) {
try {
const user = await User.findByIdAndUpdate(
id,
updateData,
{ new: true, runValidators: true }
);
if (!user) {
throw new mongoose.Error.DocumentNotFoundError();
}
return user;
} catch (error) {
if (error instanceof mongoose.Error.CastError && error.path === '_id') {
throw new Error('Invalid user ID format');
}
if (error instanceof mongoose.Error.ValidationError) {
const validationErrors = Object.values(error.errors).map(err => err.message);
throw new Error(`Validation failed: ${validationErrors.join(', ')}`);
}
throw error; // Re-throw other errors
}
}// Error type union for comprehensive error handling
type MongooseErrorTypes =
| MongooseError
| ValidationError
| ValidatorError
| CastError
| DocumentNotFoundError
| VersionError
| ParallelSaveError
| MissingSchemaError
| OverwriteModelError
| MongooseServerSelectionError
| MongooseTimeoutError
| DisconnectedError;
interface ErrorHandlerOptions {
/** Transform error before throwing */
transform?: (error: Error) => Error;
/** Custom error messages by error type */
messages?: { [errorType: string]: string };
/** Additional context for error reporting */
context?: any;
}
// Error event types for connection monitoring
interface ConnectionErrorEvents {
'error': (error: Error) => void;
'disconnected': () => void;
'reconnectFailed': () => void;
'timeout': () => void;
'close': () => void;
}