denoland/deno · error · Error
test was expected to fail but passed
Error message
test was expected to fail but passed
What it means
A test registered with the expectFailure option inverts the pass/fail semantics: the runner expects the body to throw (runPossiblyExpectingFailure catches and, when expectFailure is a pattern/predicate, checks the error matches). If the body runs to completion without error, `failed` stays false and the runner throws 'test was expected to fail but passed'. So with this option, a passing test body is itself the failure.
Source
Thrown at ext/node/polyfills/testing.ts:1334
}
}
let failed = false;
try {
await runWithTestGuards(
() => runNodeTestFunction(fn, nodeTestContext),
guards,
);
nodeTestContext._checkPlan();
} catch (err) {
failed = true;
assertExpectedFailure(err, options.expectFailure);
} finally {
await nodeTestContext._drainSubtests();
}
if (!failed) {
throw new Error("test was expected to fail but passed");
}
return undefined;
}
class TestPlan {
#expected;
#actual = 0;
constructor(count) {
this.#expected = count;
}
increment() {
this.#actual++;
}
check() {
if (this.#actual !== this.#expected) {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- If the underlying bug is fixed, remove the expectFailure option and assert success directly
- If the test must still fail, make the body actually throw: assert.fail(), assert.throws(...), or await assert.rejects(...)
- Audit the body for swallowed errors — missing await on async calls, .catch(() => {}), or try/catch around the formerly-throwing line
- If expectFailure is a RegExp/string/predicate, confirm the intended failure actually matches it — and that it still occurs
Example fix
// before
test('parses bad input', { expectFailure: true }, () => {
parse('bad'); // bug was fixed; no longer throws
});
// after
test('parses bad input', () => {
assert.throws(() => parse('bad'), SyntaxError);
}); Defensive patterns
Strategy: validation
Validate before calling
// dev-time dry run: confirm the body actually throws before flagging expectFailure
async function expectFailTest(name, fn) {
let threw = false;
try {
await fn();
} catch {
threw = true;
}
if (!threw) {
throw new Error(`refusing to register expectFailure: '${name}' body does not throw`);
}
test(name, { expectFailure: true }, fn);
} Prevention
- Remove expectFailure as part of the same change that fixes the bug it documents
- Audit expectFailure bodies for swallowed errors (missing await, empty catch)
- When expectFailure carries a pattern, also assert the error shape in a separate passing test
When it happens
Trigger: test('name', { expectFailure: true }, fn) where fn completes normally; the same with expectFailure: /pattern/ or a predicate, since a body that does not throw produces no error to match; async bodies whose rejection is accidentally swallowed (missing await, empty catch).
Common situations: The bug the test documented got fixed, so the body no longer throws but the expectFailure flag was never removed; TDD red tests left flagged after going green; refactors that wrapped the throwing call in a try/catch or optional chaining that now hides the error.
Related errors
- ERR_INVALID_ARG_TYPE
- plan expected ${this.#expected} assertion(s) but received ${
- before() requires a function
- after() requires a function
- beforeEach() requires a function
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/8d4db10e6b1dbec5.
Report an issue: GitHub.