caolan/async · error · Error
expected a function
Error message
expected a function
What it means
wrapAsync normalizes a task/iteratee argument that may be callback-style, an async function, or a promise-returning function. If the value passed where a function is expected is not callable at all, it throws 'expected a function' immediately. This is the entry-point validation for nearly every async.js control-flow helper.
Source
Thrown at lib/internal/wrapAsync.js:16
import asyncify from '../asyncify.js'
function isAsync(fn) {
return fn[Symbol.toStringTag] === 'AsyncFunction';
}
function isAsyncGenerator(fn) {
return fn[Symbol.toStringTag] === 'AsyncGenerator';
}
function isAsyncIterable(obj) {
return typeof obj[Symbol.asyncIterator] === 'function';
}
function wrapAsync(asyncFn) {
if (typeof asyncFn !== 'function') throw new Error('expected a function')
return isAsync(asyncFn) ? asyncify(asyncFn) : asyncFn;
}
export default wrapAsync;
export { isAsync, isAsyncGenerator, isAsyncIterable };
View on GitHub (pinned to 13dfaf13f3)
Solutions
- Verify the argument is a function before calling: console.log(typeof fn)
- Fix the import — ensure the helper is exported and imported by the right name
- Pass the function reference, not its invocation result
- Bind detached methods: obj.method.bind(obj)
Example fix
// before await async.each(keys, processKey(), cb); // invoked // after await async.each(keys, processKey, cb);
Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof iteratee !== 'function') throw new TypeError('iteratee must be a function'); Type guard
function isCallable(v) {
return typeof v === 'function';
} Try / catch
try {
await async.each(coll, iteratee);
} catch (err) {
if (String(err.message) === 'expected a function') {
console.error('Check the iteratee/task import and reference');
} else throw err;
} Prevention
- Pass function references, not invocation results
- Verify imports exist at call time (watch for circular-import undefined)
- typeof-check dynamically supplied task functions before handing them to async
When it happens
Trigger: async.map(coll, undefined, cb), passing a method that was detached from its object and is undefined, passing a promise or result object instead of the function, a typo'd import (async.waterfall(coll, notImported)), passing null as the iteratee.
Common situations: Bad imports/circular imports leaving the function undefined at call time, destructuring a module property that doesn't exist, passing an already-invoked function's result instead of the function itself (fn() vs fn), minified code misreferencing symbols.
Related errors
- async.auto task `${key}` has a non-existent dependency `${de
- autoInject task functions require explicit parameters.
- task callback must be a function
- Invalid arguments for async.retry
- async.auto cannot execute tasks due to a recursive dependenc
AI-assisted analysis of caolan/async@13dfaf13f3 (2026-08-28).
Data as JSON: /api/errors/f7d5b4209db5b866.
Report an issue: GitHub.