{"record":{"id":"597da09158f8e4df","repo":"caolan/async","slug":"task-callback-must-be-a-function","errorCode":null,"errorMessage":"task callback must be a function","messagePattern":"task callback must be a function","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/internal/queue.js","lineNumber":50,"sourceCode":"            handler(...args)\n        }\n        events[event].push(handleAndRemove)\n    }\n\n    function off (event, handler) {\n        if (!event) return Object.keys(events).forEach(ev => events[ev] = [])\n        if (!handler) return events[event] = []\n        events[event] = events[event].filter(ev => ev !== handler)\n    }\n\n    function trigger (event, ...args) {\n        events[event].forEach(handler => handler(...args))\n    }\n\n    var processingScheduled = false;\n    function _insert(data, insertAtFront, rejectOnError, callback) {\n        if (callback != null && typeof callback !== 'function') {\n            throw new Error('task callback must be a function');\n        }\n        q.started = true;\n\n        var res, rej;\n        function promiseCallback (err, ...args) {\n            // we don't care about the error, let the global error handler\n            // deal with it\n            if (err) return rejectOnError ? rej(err) : res()\n            if (args.length <= 1) return res(args[0])\n            res(args)\n        }\n\n        var item = q._createTaskItem(\n            data,\n            rejectOnError ? promiseCallback :\n                (callback || promiseCallback)\n        );\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/caolan/async/blob/13dfaf13f3fc809ba1c9c39d5e267ac6959cbf4a/lib/internal/queue.js#L32-L68","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a function as the second argument: q.push(task, (err) => {...})","Omit the second argument entirely if no per-task callback is needed","Check that the variable holding the callback actually holds a function","Use q.drain / promise-returning push (pushAsync) instead of ad-hoc callbacks"],"exampleFix":"// before\nq.push(task, 'handleResult');\n// after\nq.push(task, (err) => { if (err) console.error(err); });","handlingStrategy":"type-guard","validationCode":"const isCallback = (v) => v == null || typeof v === 'function';\nif (!isCallback(cb)) throw new TypeError('task callback must be a function');","typeGuard":"function isFunction(v) {\n  return typeof v === 'function';\n}","tryCatchPattern":"try {\n  q.push(task, cb);\n} catch (err) {\n  if (String(err.message).includes('callback must be a function')) {\n    console.error('Second argument to push must be a function or omitted');\n  } else throw err;\n}","preventionTips":["Only pass functions (or nothing) as the second argument to push/unshift","Use pushAsync with promises for per-task completion","Type-check callback variables after refactors"],"tags":["async","queue","type-error"],"backgroundTag":"callback-not-a-function","analyzedSha":"13dfaf13f3fc809ba1c9c39d5e267ac6959cbf4a","analyzedAt":"2026-08-28T22:50:46.507Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}