avajs/ava · error · AssertionError

message

Error message

message

What it means

Thrown by assertExpectations in lib/assert.js:167 when the assertion observed a thrown value that is not a native Error and the expectations did not include `any: true`. The error message is the assertion's own message (e.g. 'Function threw a non-Error' style from the t.throws wrapper); the actual thrown value is attached as `cause` and shown under a '<prefix> exception that is not an error:' label. This keeps throws assertions strict: strings, numbers, and objects thrown ad hoc are rejected unless explicitly allowed.

Source

Thrown at lib/assert.js:167

				default: {
					throw new AssertionError(`The second argument to \`${assertion}\` contains unexpected properties`, {
						assertion,
						formattedDetails: [formatWithLabel('Called with:', expectations)],
					});
				}
			}
		}
	}

	return expectations;
}

// Note: this function *must* throw exceptions, since it can be used
// as part of a pending assertion for promises.
function assertExpectations({actual, expectations, message, prefix, assertion, assertionStack}) {
	const allowThrowAnything = Object.hasOwn(expectations, 'any') && expectations.any;
	if (!isNativeError(actual) && !allowThrowAnything) {
		throw new AssertionError(message, {
			assertion,
			assertionStack,
			cause: actual,
			formattedDetails: [formatWithLabel(`${prefix} exception that is not an error:`, actual)],
		});
	}

	if (Object.hasOwn(expectations, 'is') && actual !== expectations.is) {
		throw new AssertionError(message, {
			assertion,
			assertionStack,
			cause: actual,
			formattedDetails: [
				formatWithLabel(`${prefix} unexpected exception:`, actual),
				formatWithLabel('Expected to be strictly equal to:', expectations.is),
			],
		});
	}

View on GitHub (pinned to bbfd946322)

Solutions

  1. If accepting non-Error throwables is intended, add `any: true`: `t.throws(fn, {any: true})`.
  2. Preferably fix the code under test to `throw new Error('boom')` or reject with an Error instance.
  3. If you expected an Error, check what is actually thrown via the `cause` / formatted details and adjust the code under test.

Example fix

// before
t.throwsAsync(async () => http.get('/x'));

// after
t.throwsAsync(async () => http.get('/x'), { any: true });
// or fix the source: throw new Error('request failed');
Defensive patterns

Strategy: try-catch

Validate before calling

async function rejectsWithError(fn) {
  try {
    await fn();
  } catch (e) {
    if (!(e instanceof Error) && !Object.hasOwn(expectations ?? {}, 'any')) {
      throw new TypeError('code under test throws a non-Error; use {any: true}');
    }
    return e;
  }
  throw new Error('expected a throw/rejection');
}

Type guard

function isError(v) {
  return v instanceof Error || Object.prototype.toString.call(v) === '[object Error]';
}

Try / catch

try {
  await t.throwsAsync(fn, {any: true});
} catch (error) {
  if (error.name === 'AssertionError' && /not an error/.test(error.message)) {
    console.error('Threw non-Error:', error.cause);
  }
  throw error;
}

Prevention

When it happens

Trigger: Code under test does `throw 'boom'`, `throw {msg: 'boom'}`, `throw undefined`, or rejects with a non-Error, while calling t.throws(fn) / t.throwsAsync(p) without `{any: true}`.

Common situations: Legacy code that throws raw strings; promise rejections with plain objects (`Promise.reject({reason: ...})`); testing wrappers around APIs that reject with non-Error values (e.g. some fetch wrappers); Node util.isError vs class-hierarchy confusion where a fake error class is used.

Related errors


AI-assisted analysis of avajs/ava@bbfd946322 (2026-09-02). Data as JSON: /api/errors/3fd3d0141b7ea563. Report an issue: GitHub.