How to resolve the Mocha "Timeout of 2000ms exceeded" Error in JavaScript
When writing asynchronous tests in Mocha, a common and frustrating error is: "Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves."
This error means your test took too long to finish. It's Mocha's safety mechanism to prevent tests from running forever. This guide will explain the two fundamental rules of async testing in Mocha and provide a clear checklist to help you fix this error.
The Two Golden Rules of Async Testing in Mocha
Mocha needs to know when your asynchronous test is finished. It gives you two ways to signal this, and you must choose one and only one:
Rule 1: The done Callback
If your test function accepts an argument (conventionally named done), Mocha will wait until you explicitly call done() to finish the test. This is the traditional way to handle callbacks.
it('should complete an async task', (done) => {
setTimeout(() => {
// Assertions go here
done(); // You MUST call done() to finish the test.
}, 100);
});
Rule 2: Return a Promise
If your test function is marked as async or returns a Promise, Mocha will wait for that promise to resolve or reject. You do not use the done callback in this case.
it('should complete an async task', async () => {
const result = await someAsyncFunction();
// Assertions go here
// NO done() call is needed.
});
The timeout error is almost always a result of violating one of these two rules.
Your Debugging Checklist
Follow these steps to diagnose and fix the timeout error.
Step 1: Did you forget to call done()? (for callbacks)
This is the most common cause of the error when using the callback pattern. If you accept the done argument, you are responsible for calling it.
Example of code with problems:
it('tests a promise but forgets to call done', (done) => {
const p = Promise.resolve('success');
p.then(result => {
// Assertions are made...
assert.strictEqual(result, 'success');
// ...but done() is never called, so Mocha waits forever.
});
});
Solution: ensure done() is called after all asynchronous operations and assertions are complete. It's also a best practice to pass done to your .catch() block to fail the test immediately if the promise rejects.
it('correctly calls done()', (done) => {
const p = Promise.resolve('success');
p.then(result => {
assert.strictEqual(result, 'success');
done(); // Correctly signals completion
}).catch(done); // Fails the test if the promise rejects
});
Step 2: Are you mixing async/await with done?
This is the second most common mistake. If you declare your test function as async, you should not also accept the done argument. Mocha sees the done argument and switches to callback mode, ignoring the promise that async/await returns.
Example of code with problem:
// ❌ INCORRECT: Mixing async and done
it('mixes async with the done callback', async (done) => {
const result = await Promise.resolve('success');
assert.strictEqual(result, 'success');
// Because `done` is never called, the test times out.
});
If you were to call done() here, Mocha would throw a different error: "Resolution method is overspecified."
As solution, when using async/await, simply remove the done argument from your test function.
// ✅ CORRECT: Using async/await without done
it('correctly uses async/await', async () => {
const result = await Promise.resolve('success');
assert.strictEqual(result, 'success');
// No done() is needed. Mocha waits for the promise to resolve.
});
Step 3: Is your test simply running too slow?
If your asynchronous logic is correct but the operation legitimately takes longer than Mocha's default timeout (2000ms), you will need to increase it.
Example of code with problem:
it('takes a long time to finish', async () => {
// This operation takes 3 seconds, which is longer than the default 2-second timeout.
const result = await new Promise(resolve => setTimeout(() => resolve('done'), 3000));
assert.strictEqual(result, 'done');
});
As solution, you can increase the timeout in several ways:
1. Per-Test (Recommended): Chain the .timeout() method to your test case.
it('takes a long time', async () => {
// ...
}).timeout(5000); // Set timeout to 5000ms for this test only
2. Per-File: Use this.timeout() inside a describe block (note: you cannot use an arrow function here).
describe('Slow Tests', function() {
this.timeout(5000); // Sets timeout for all tests in this block
it('is a slow test', async () => { /* ... */ });
});
3. Globally (via command line): Use the --timeout flag when running Mocha.
mocha --timeout 5000
Conclusion
The Mocha "Timeout of 2000ms exceeded" error is a clear signal that your asynchronous test is not completing correctly.
- Remember the two golden rules: either use the
donecallback OR return a promise (by usingasync/await), but never both. - If you use the
donecallback, ensure it is always called, even in error cases. - If you use
async/await, do not accept thedoneargument. - If your test is structured correctly but is simply slow, increase the timeout using
.timeout()or the--timeoutflag.