skeggse skeggse - 3 months ago 40
Node.js Question

Node.js Uncaught TypeError: callback is not a function in process.nextTick

I'm getting the following error. It's clearly coming from a callback passed to

process.nextTick
. Given that the stack trace is virtually unusable, how do I debug this? What's going on behind the scenes, and how can I resolve this in a larger project?

TypeError: callback is not a function
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickDomainCallback (node.js:390:13)


N.B. Older versions of Node had a different implementation of
process.nextTick
, and spit out the following stack trace.

Uncaught TypeError: undefined is not a function
at process._tickCallback (node.js:415:13)

Answer

Node.js tick callback prefers to throw an error if the function you pass to process.nextTick is not a function after a tick has passed, rather than telling you when you first call it.

Where a function will just get called after a tick:

// this will print "hi!", followed by "hello there"
process.nextTick(function() {
  console.log('hello there');
});
console.log('hi!');

A non-function won't actually matter until the next tick:

// this will just print "hi!", and an error will be thrown later on
process.nextTick(undefined);
console.log('hi!');

Below, I'll outline two diagnostic tools you can use to track down the culprit.

Tool 1: longjohn

As it turns out, this kind of scenario is common enough that a tool exists to extend stack traces with information from previous ticks.

Simply install longjohn (available on npm), and require it when your process starts. This will yield a stack trace similar to the following:

TypeError: Cannot read property 'apply' of undefined
    at nextTickCallbackWith0Args (node.js:420:9)
    at process._tickCallback (node.js:349:13)
    at Function.Module.runMain (module.js:443:11)
    at startup (node.js:139:18)
    at node.js:968:3
---------------------------------------------
    at Object.<anonymous> (.../example.js:3:9)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:139:18)
    at node.js:968:3

Now that you have a useful stack trace, you can use your debugging skills to figure out why you're giving process.nextTick undefined rather than a function.

Monkey patching

Because javascript is dynamic, we can easily setup a new version of process.nextTick (that is, we can monkey patch it) which traces the call to it, but proxies to the real nextTick function:

var nextTick = process.nextTick;

process.nextTick = function(callback) {
  if (typeof callback !== 'function') {
    console.trace(typeof callback + ' is not a function');
  }
  return nextTick.apply(process, arguments);
};

Note: I don't recommend doing this for production code, but it does make it much easier to locate where you're providing something besides a function to process.nextTick. Remember that this is modifying the global process variable, so it might break at some point and it's certainly not considered best practice, but it'll do nicely for finding small problems. (Addendum: and since I originally wrote this post, I've had to revise it because it broke once.)

Trace: undefined is not a function
    at process.nextTick (.../example.js:5:13)
    at Object.<anonymous> (.../example.js:10:9)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:139:18)
    at node.js:968:3

As above, now that you have a useful stack trace, you can identify the root cause.

In terms of the internals, check out the _tickCallback function in node.js in the source (old link).

Comments