mochajs/mocha · warning · PendingError
sync skip; aborting execution
Error message
sync skip; aborting execution
What it means
`Runnable.prototype.skip()` marks the test as pending and throws a `PendingError` to immediately abort the test body's synchronous execution; Mocha's internal error handler recognizes it and reports the test as skipped rather than failed. Seeing this message as a raw failure means the pending sentinel escaped Mocha's handling.
Source
Thrown at lib/runnable.js:140
return this._slow;
}
if (typeof ms === "string") {
ms = milliseconds(ms);
}
debug("slow %d", ms);
this._slow = ms;
return this;
}
/**
* Halt and mark as pending.
*
* @memberof Mocha.Runnable
* @public
*/
skip() {
this.pending = true;
throw new PendingError("sync skip; aborting execution");
}
/**
* Check if this runnable or its parent suite is marked as pending.
*
* @private
*/
isPending() {
return this.pending || (this.parent && this.parent.isPending());
}
/**
* Return `true` if this Runnable has failed.
* @return {boolean}
* @private
*/
isFailed() {
return !this.isPending() && this.state === Runnable.constants.STATE_FAILED;View on GitHub (pinned to 6bcbee4fd9)
Solutions
- Prefer `it.skip`/`describe.skip` or `this.skip()` only inside a running test/hook where Mocha can intercept it.
- Don't catch-and-rethrow errors thrown inside test bodies without re-checking `err instanceof PendingError` (or checking `this.isPending()`).
- For conditional skipping in async tests, use the async pattern (`this.skip()` in a promise chain) or set `this.pending` via a helper that Mocha supports.
- If this surfaces as a test failure rather than a skip, check for custom wrappers/uncaught handlers swallowing or rethrowing PendingError.
Example fix
// before
wrap(function (done) {
if (!support) this.skip(); // rethrown by wrapper -> surfaces as failure
});
// after
it('does x', function () {
if (!support) return this.skip();
// rest of test
}); Defensive patterns
Strategy: try-catch
Validate before calling
if (this.isPending?.()) return; // already skipped, don't call skip again
if (!ctx || typeof ctx.skip !== 'function') {
throw new Error('this.skip() only valid inside a runnable test/hook');
} Type guard
const isPendingError = (err) => err instanceof Error && (err.message === 'sync skip; aborting execution' || err.message === 'async skip; aborting execution');
Try / catch
try {
runTestBody();
} catch (err) {
if (isPendingError(err)) {
markTestPending(test);
return; // treat as skip, not failure
}
throw err;
} Prevention
- Call this.skip() only inside a running test body or hook.
- Prefer static it.skip/describe.skip for compile-time-known skips.
- Avoid custom wrappers that catch and rethrow test-body errors blindly.
- Return immediately after this.skip() so no code runs afterward.
When it happens
Trigger: Calling `this.skip()` inside a test body or hook; Mocha's `--bail`/uncaught-exception plumbing failing to swallow the PendingError (e.g. `this.skip()` used where no runnable context exists); user code catching and rethrowing the sentinel across a boundary Mocha doesn't inspect.
Common situations: Dynamic skipping in tests (`if (!condition) this.skip()`); calling `this.skip()` inside a `before` hook of a suite-level skip; wrapping test functions in custom async wrappers that rethrow errors after Mocha's handler ran.
Related errors
AI-assisted analysis of mochajs/mocha@6bcbee4fd9 (2026-09-01).
Data as JSON: /api/errors/9b5bdd208f2feea1.
Report an issue: GitHub.