denoland/deno · error · AssertionError
ERR_ASSERTION
ERR_ASSERTION
Error message
${actual} ${operator} ${expected} What it means
innerFail is the terminal branch of node:assert when a comparison fails (ext/node/polyfills/assert.ts:130): if the caller supplied an Error object as the message, that error is rethrown as-is; otherwise it throws an AssertionError built from { actual, expected, message, operator, stackStartFn, diff }. The '${actual} ${operator} ${expected}' rendering is the AssertionError's generated message format, so this entry fires on every failed assertion that reaches innerFail (e.g. assert.throws mismatches routed through compareExceptionKey).
Source
Thrown at ext/node/polyfills/assert.ts:130
Assert.prototype.deepStrictEqual = deepStrictEqual;
Assert.prototype.notDeepStrictEqual = notDeepStrictEqual;
Assert.prototype.strictEqual = strictEqual;
Assert.prototype.notStrictEqual = notStrictEqual;
Assert.prototype.partialDeepStrictEqual = partialDeepStrictEqual;
Assert.prototype.throws = throws;
Assert.prototype.rejects = rejects;
Assert.prototype.doesNotThrow = doesNotThrow;
Assert.prototype.doesNotReject = doesNotReject;
Assert.prototype.ifError = ifError;
Assert.prototype.match = match;
Assert.prototype.doesNotMatch = doesNotMatch;
function innerFail(obj) {
if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, obj.message)) {
throw obj.message;
}
throw new AssertionError({
actual: obj.actual,
expected: obj.expected,
message: obj.message,
operator: obj.operator,
stackStartFn: obj.stackStartFn,
diff: obj.diff,
});
}
function assert(...args) {
innerOk(ok, args.length, ...new SafeArrayIterator(args));
}
const ok = assert;
class Comparison {
constructor(obj, keys, actual) {
for (const key of new SafeArrayIterator(keys)) {
if (ReflectHas(obj, key)) {View on GitHub (pinned to 89f33cbef2)
Solutions
- Inspect the AssertionError's actual/expected fields (or the diff) to see the divergence, then fix the code or the expectation
- In assert.throws validation objects, list only properties that must match (e.g. { name: 'TypeError', message: /boom/ }) instead of deep-equal whole errors
- Add assert.fail('why') with explicit messages at branch points so failures surface intent, not just values
Example fix
// before
assert.throws(() => parse('nope'), { message: 'exactly this' }); // message differs
// after
assert.throws(() => parse('nope'), { name: 'SyntaxError', message: /nope/ }); Defensive patterns
Strategy: try-catch
Try / catch
try { assert.strictEqual(actual, expected, 'context: why these must match'); } catch (e) { if (e.code === 'ERR_ASSERTION') { /* inspect e.actual/e.expected, fix code or expectation */ throw e; } throw e; } Prevention
- Give assertions message arguments describing intent
- Match on selected properties (name/message regex) rather than whole error objects
When it happens
Trigger: assert.strictEqual(1, 2); assert.throws(fn, { code: 'X' }) where fn threw no matching property; assert(fn, customMessageNotProvided); any failed ok/equals family assertion in strict mode routed via innerOk -> innerFail.
Common situations: Ordinary test failures — regressions where the value under test changed; overly loose expected objects in assert.throws that mismatch a property; NaN comparisons failing strict equality; async timing changing observed state between runs.
Related errors
- ERR_CONSTRUCT_CALL_REQUIRED
- ERR_INVALID_ARG_VALUE
- ERR_INVALID_RETURN_VALUE
- ERR_INVALID_ARG_TYPE
- ERR_AMBIGUOUS_ARGUMENT
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/ea4211b9a86d7a75.
Report an issue: GitHub.