Sửa lỗi MongoDB 'ns not found' Khi Gọi drop() hoặc dropIndex()

beginner🍃 MongoDB2026-06-20| MongoDB 4.x–7.x, Node.js (Mongoose/MongoDB Node Driver), Python (PyMongo), MongoDB Shell (mongosh / legacy mongo)

Error Message

MongoError: ns not found
#mongodb#drop#dropIndex#collection#namespace

Lỗi Gặp Phải

Bạn gọi db.myCollection.drop() hoặc db.myCollection.dropIndex('some_index') và MongoDB trả về lỗi:

MongoError: ns not found

Các driver mới hơn và mongosh hiển thị lỗi với cú pháp hơi khác:

MongoServerError: ns not found
MongoError: ns not found (codeName: 'NamespaceNotFound', code: 26)

Thao tác dừng hẳn — không có cleanup một phần, không có fallback.

Nguyên Nhân

Namespace bạn cố drop không tồn tại. Đơn giản vậy thôi. MongoDB không tự động bỏ qua các namespace không tồn tại như cách DROP TABLE IF EXISTS của SQL làm — trừ khi bạn chỉ định rõ ràng.

Một số tình huống thường gây ra lỗi này:

  • Chạy migration hoặc seed script hai lần — lần chạy thứ hai cố drop thứ mà lần đầu đã xóa rồi.
  • Teardown trong test drop một collection chưa bao giờ được tạo vì quá trình setup bị lỗi giữa chừng.
  • Gõ nhầm tên collection — ví dụ user thay vì users.
  • Gọi dropIndex() sau khi một script trước đó đã xóa index đó rồi.
  • Drop collection từ một database vừa được xóa sạch trong môi trường CI.

Cách Sửa

1. MongoDB Shell — Kiểm Tra Trước Khi Drop

Cách tiếp cận an toàn nhất với mongosh: xác minh collection tồn tại trước khi thao tác.

if (db.getCollectionNames().includes('myCollection')) {
  db.myCollection.drop();
  print('Đã drop.');
} else {
  print('Collection không tồn tại, bỏ qua.');
}

Tương tự với index — lấy danh sách index hiện tại trước:

const indexes = db.myCollection.getIndexes().map(i => i.name);
if (indexes.includes('email_1')) {
  db.myCollection.dropIndex('email_1');
}

2. Node.js — MongoDB Native Driver

Thay vì kiểm tra trước, hãy bắt error code 26 (NamespaceNotFound) và bỏ qua nó. Mọi lỗi khác vẫn sẽ được throw.

const { MongoClient } = require('mongodb');

async function safeDrop(db, collectionName) {
  try {
    await db.collection(collectionName).drop();
    console.log(`Dropped ${collectionName}`);
  } catch (err) {
    if (err.code === 26) {
      console.log(`${collectionName} doesn't exist, skipping drop.`);
    } else {
      throw err;
    }
  }
}

const client = new MongoClient(process.env.MONGO_URI);
await client.connect();
const db = client.db('mydb');
await safeDrop(db, 'oldCollection');

Với dropIndex, bạn cần xử lý hai code: 26 khi bản thân collection không tồn tại, và 27 (IndexNotFound) khi collection tồn tại nhưng index đã bị xóa rồi:

async function safeDropIndex(collection, indexName) {
  try {
    await collection.dropIndex(indexName);
  } catch (err) {
    if (err.code === 26 || err.code === 27) {
      return; // already gone
    }
    throw err;
  }
}

3. Mongoose

Mongoose bọc native driver bên dưới, nên error code vẫn như vậy. Block catch cũng xử lý các phiên bản driver cũ hơn hiển thị lỗi dưới dạng chuỗi message thay vì code:

async function safeDrop(modelOrCollectionName) {
  try {
    await mongoose.connection.dropCollection(modelOrCollectionName);
  } catch (err) {
    if (err.code === 26 || err.message === 'ns not found') {
      return;
    }
    throw err;
  }
}

// Trong teardown của test
afterEach(async () => {
  await safeDrop('users');
});

4. PyMongo (Python)

drop_collection() của PyMongo tự động nuốt lỗi ns not found bên trong nội bộ ở hầu hết các phiên bản. drop_index() thì không — bạn phải tự catch.

from pymongo import MongoClient
from pymongo.errors import OperationFailure

client = MongoClient("mongodb://localhost:27017")
db = client["mydb"]

# drop_collection thường an toàn, nhưng xử lý tường minh vẫn tốt hơn
try:
    db.drop_collection("old_collection")
except OperationFailure as e:
    if e.code == 26:
        print("Collection không tồn tại, bỏ qua.")
    else:
        raise

# drop_index cần xử lý thủ công
try:
    db.my_collection.drop_index("email_1")
except OperationFailure as e:
    if e.code in (26, 27):  # 26 = collection missing, 27 = index missing
        pass
    else:
        raise

5. Migration Idempotent

CI pipeline thường chạy lại migration khi rollback và retry. Hãy bọc mọi bước có tính hủy hoại để các lần chạy lại không thất bại lúc 2 giờ sáng:

// migration_001_drop_old_index.js
module.exports = {
  async up(db) {
    const col = db.collection('orders');
    try {
      await col.dropIndex('status_1_createdAt_1');
    } catch (err) {
      if (err.code !== 26 && err.code !== 27) throw err;
    }
  },
  async down(db) {
    await db.collection('orders').createIndex(
      { status: 1, createdAt: 1 },
      { name: 'status_1_createdAt_1' }
    );
  }
};

Kiểm Tra Sau Khi Sửa

Chạy script hai lần liên tiếp. Không có lỗi ở lần chạy thứ hai nghĩa là wrapper đang hoạt động đúng.

Kiểm tra trạng thái collection và index trực tiếp trong mongosh:

// Liệt kê tất cả collection trong database hiện tại
db.getCollectionNames()

// Xác nhận target đã bị xóa
db.getCollectionNames().includes('oldCollection') // → false

// Kiểm tra các index còn lại trên một collection
db.myCollection.getIndexes()

Phòng Tránh

  • Khớp theo error code, không phải chuỗi message. MongoDB đã thay đổi nội dung thông báo lỗi qua các phiên bản; các code số — 26 cho NamespaceNotFound, 27 cho IndexNotFound — chưa bao giờ thay đổi.
  • Làm cho mọi migration idempotent ngay từ đầu. Bọc một lệnh drop tốn hai dòng code. Debug một lần chạy lại CI thất bại tốn nhiều hơn thế.
  • Đừng giả định teardown đã chạy sạch trong test. Dùng beforeEach để setup dữ liệu mới thay vì phụ thuộc vào việc cleanup của test trước đó thành công.
  • Kiểm tra kỹ tên collection. userusers gây ra đúng cùng một lỗi và rất dễ bỏ sót.

Related Error Notes