caolan/async · error · Error

task callback must be a function

Error message

task callback must be a function

What it means

When pushing tasks to a queue (or cargo), any non-null callback supplied after the task data must be a function; q.push(data, 'done') or push(data, 42) throws immediately. This validates the per-task completion callback before scheduling work.

Source

Thrown at lib/internal/queue.js:50

            handler(...args)
        }
        events[event].push(handleAndRemove)
    }

    function off (event, handler) {
        if (!event) return Object.keys(events).forEach(ev => events[ev] = [])
        if (!handler) return events[event] = []
        events[event] = events[event].filter(ev => ev !== handler)
    }

    function trigger (event, ...args) {
        events[event].forEach(handler => handler(...args))
    }

    var processingScheduled = false;
    function _insert(data, insertAtFront, rejectOnError, callback) {
        if (callback != null && typeof callback !== 'function') {
            throw new Error('task callback must be a function');
        }
        q.started = true;

        var res, rej;
        function promiseCallback (err, ...args) {
            // we don't care about the error, let the global error handler
            // deal with it
            if (err) return rejectOnError ? rej(err) : res()
            if (args.length <= 1) return res(args[0])
            res(args)
        }

        var item = q._createTaskItem(
            data,
            rejectOnError ? promiseCallback :
                (callback || promiseCallback)
        );

View on GitHub (pinned to 13dfaf13f3)

Solutions

  1. Pass a function as the second argument: q.push(task, (err) => {...})
  2. Omit the second argument entirely if no per-task callback is needed
  3. Check that the variable holding the callback actually holds a function
  4. Use q.drain / promise-returning push (pushAsync) instead of ad-hoc callbacks

Example fix

// before
q.push(task, 'handleResult');
// after
q.push(task, (err) => { if (err) console.error(err); });
Defensive patterns

Strategy: type-guard

Validate before calling

const isCallback = (v) => v == null || typeof v === 'function';
if (!isCallback(cb)) throw new TypeError('task callback must be a function');

Type guard

function isFunction(v) {
  return typeof v === 'function';
}

Try / catch

try {
  q.push(task, cb);
} catch (err) {
  if (String(err.message).includes('callback must be a function')) {
    console.error('Second argument to push must be a function or omitted');
  } else throw err;
}

Prevention

When it happens

Trigger: q.push(task, undefined-as-string variables), passing a truthy non-function such as the string 'cb', a thenable, or an object where a function was intended: q.push(item, somePromise). Also q.unshift with the same mistake.

Common situations: Argument-order mistakes when wrapping push, refactors where the callback variable was reassigned, passing options object as second arg mistakenly believing it's a callback, TypeScript types bypassed with any.

Related errors


AI-assisted analysis of caolan/async@13dfaf13f3 (2026-08-28). Data as JSON: /api/errors/597da09158f8e4df. Report an issue: GitHub.