avajs/ava · error · TypeError
Expected a number
Error message
Expected a number
What it means
t.plan(count) sets the expected number of assertions for a test or attempt. AVA validates that the argument is a number; anything else (string, undefined, NaN-free objects, etc.) is rejected with this TypeError so assertion-count accounting stays correct.
Source
Thrown at lib/test.js:419
for (const log of logs) {
this.addLog(log);
}
}
this.refreshTimeout();
if (this.testFailure) {
throw this.testFailure;
}
}
saveFirstError(error) {
this.assertError ??= error;
this.testFailure = new TestFailure();
}
plan(count, planAssertionStack) {
if (typeof count !== 'number') {
throw new TypeError('Expected a number');
}
this.planCount = count;
// In case the `planCount` doesn't match `assertCount, we need the stack of
// this function to throw with a useful stack.
this.planAssertionStack = planAssertionStack;
}
timeout(ms, message) {
const result = checkAssertionMessage(message, 't.timeout()');
if (result !== true) {
this.saveFirstError(result);
// Allow the timeout to be set even when the message is invalid.
message = '';
}
if (this.finishing) {View on GitHub (pinned to bbfd946322)
Solutions
- Pass a number literal or Number-coerced value: t.plan(3) or t.plan(Number(raw)).
- Validate the source value before calling: if (typeof n !== 'number') throw ... or use Number.parseInt + Number.isFinite.
- Remove t.plan() if assertion counting is not needed — planning is optional.
Example fix
// before t.plan(process.env.PLAN_COUNT); // string -> TypeError // after const count = Number.parseInt(process.env.PLAN_COUNT, 10); if (Number.isFinite(count)) t.plan(count);
Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof count !== 'number' || !Number.isInteger(count) || count < 0) throw new TypeError('t.plan() expects a non-negative integer'); Type guard
const isPlanCount = (v) => typeof v === 'number' && Number.isInteger(v) && v >= 0;
Try / catch
try {
t.plan(count);
} catch (err) {
if (err instanceof TypeError && err.message === 'Expected a number') {
t.plan(Number(count) || 0);
} else throw err;
} Prevention
- Coerce env/config values with Number() before t.plan()
- Pass numeric literals directly where possible
- Enable strict typing on wrapper helpers around t.plan
When it happens
Trigger: t.plan('3'); t.plan(someVariable) where the variable is undefined; t.plan(count) where count comes from parseInt() of bad input or a config value; forwarding arguments to t.plan without validation.
Common situations: Reading the planned count from an env var or CLI arg (always a string); a helper function passing optional parameters through to t.plan; refactor changed the variable's type.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Expected an implementation. Use `test.todo()` for tests with
- Test & hook titles must be strings
- Tests must have a title
- Expected an implementation.
- `t.try()` titles must be strings
AI-assisted analysis of avajs/ava@bbfd946322 (2026-09-02).
Data as JSON: /api/errors/f60f1524aea332ab.
Report an issue: GitHub.