denoland/deno · error · TypeError
ERR_INVALID_RETURN_VALUE
ERR_INVALID_RETURN_VALUE
Error message
Expected instance of Promise to be returned from the "promiseFn" function but got ${resultPromise}. What it means
Thrown by assert.rejects() and assert.doesNotReject() when the first argument is a function whose return value is not a Promise. waitForActual() invokes promiseFn() and immediately checks the result with checkIsPromise(); a plain value (including the undefined returned by a non-async function) fails that check, so the error is raised before any assertion logic runs.
Source
Thrown at ext/node/polyfills/assert.ts:353
// Accept native ES6 promises and promises that are implemented in a similar
// way. Do not accept thenables that use a function as `obj` and that have no
// `catch` handler.
return isPromise(obj) ||
(obj !== null && typeof obj === "object" &&
typeof obj.then === "function" &&
typeof obj.catch === "function");
}
async function waitForActual(
promiseFn,
) {
let resultPromise;
if (typeof promiseFn === "function") {
// Return a rejected promise if `promiseFn` throws synchronously.
resultPromise = promiseFn();
// Fail in case no promise is returned.
if (!checkIsPromise(resultPromise)) {
throw new ERR_INVALID_RETURN_VALUE(
"instance of Promise",
"promiseFn",
resultPromise,
);
}
} else if (checkIsPromise(promiseFn)) {
resultPromise = promiseFn;
} else {
throw new ERR_INVALID_ARG_TYPE(
"promiseFn",
["Function", "Promise"],
promiseFn,
);
}
try {
await resultPromise;
} catch (e) {View on GitHub (pinned to 89f33cbef2)
Solutions
- Make the callback async: assert.rejects(async () => { ... }) so it returns a rejected Promise
- Return a Promise explicitly from the callback (return Promise.reject(new Error(...)))
- Use assert.throws() instead when testing synchronous exceptions
- Pass a Promise directly as the first argument rather than a function
Example fix
// before
assert.rejects(() => { throw new Error('boom'); });
// after
assert.rejects(async () => { throw new Error('boom'); });
// or, for synchronous code
assert.throws(() => { throw new Error('boom'); }); Defensive patterns
Strategy: validation
Validate before calling
// Wrap the target so it always settles as a Promise const settle = (fn) => async () => fn(); await assert.rejects(settle(syncFn), /boom/);
Type guard
const isPromise = (v) => v != null && typeof v.then === 'function' && typeof v.catch === 'function';
Try / catch
try { await assert.rejects(fn, /boom/); } catch (e) { if (e.code === 'ERR_INVALID_RETURN_VALUE') throw new Error('pass an async function or a Promise to assert.rejects'); throw e; } Prevention
- Default to async () => ... callbacks in assert.rejects/assert.doesNotReject
- Reserve assert.throws for synchronous code
- Lint against non-async function arguments passed to the async assert helpers
When it happens
Trigger: assert.rejects(() => { throw new Error('boom'); }) (the callback throws synchronously and returns undefined); assert.doesNotReject(() => 42); passing any non-async helper that returns a plain value where a promise-returning function is expected.
Common situations: Using the async assertion helpers on synchronous code; forgetting the async keyword on a test helper; refactoring a function from async to sync without updating assert.rejects call sites.
Related errors
- ERR_INVALID_ARG_TYPE
- ERR_CONSTRUCT_CALL_REQUIRED
- ERR_ASSERTION
- ERR_INVALID_ARG_VALUE
- ERR_AMBIGUOUS_ARGUMENT
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/bab939eafb329a3a.
Report an issue: GitHub.