Node.js Debugging Essentials: Env Vars, Exit Codes, Deprecations & Sync I/O
Learn how to effectively debug Node.js applications by leveraging environment variables like DEBUG and NODE_DEBUG, interpreting process exit codes, handling deprecated APIs, detecting synchronous I/O, and managing unhandled promise rejections, with practical code examples and command-line flags to streamline troubleshooting.
TL;DR
This article introduces practical debugging tricks for Node.js, covering environment variables, process exit codes, deprecated API warnings, synchronous I/O detection, and unhandled promise rejections.
Debugging Environment Variables
Older Node.js versions (e.g., v0.10.x) lacked util.debuglog, so developers used the debug package. Example:
var debug = require('debug')('http'),
http = require('http'),
name = 'My App';
// fake app
debug('booting %o', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
res.end('hello
');
}).listen(3000, function(){
debug('listening');
});Setting the DEBUG environment variable to a label (e.g., http) enables logs from modules that use the debug package:
Most popular npm packages now follow this convention, making DEBUG a de‑facto standard for filtering and enabling debug output.
In the Egg.js framework, enabling DEBUG shows internal logs:
Using DEBUG=* turns on all module logs.
Node.js also provides built‑in variables:
NODE_DEBUG – enables JavaScript‑level debug logs (including util.debuglog).
NODE_DEBUG_NATIVE – enables C++‑level debug logs.
Common NODE_DEBUG switches include timer, http, net, fs, cluster, tls, stream, child_process, and module.
Example combining DEBUG and NODE_DEBUG for HTTP requests:
Process Exit Codes
When a Node.js process exits, the exit code can indicate the type of failure. Relevant codes:
1 – uncaught fatal exception.
6 – internal exception handler in a null function.
7 – internal exception handler threw an error.
128+signal – terminated by a POSIX signal (e.g., 128+6 = 134 for SIGABRT).
Example arr-crash.js that crashes with exit code 134:
// arr-crash.js
process.on('uncaughtException', () => {
console.error('Ya!');
});
const arr = [];
while (true) arr.push(1);Running echo $? after the crash returns 134, which equals 128+6, indicating the “empty function internal exception handler” case.
Another example uncaught-exception.js demonstrates exit code 7:
// uncaught-exception.js
process.on('uncaughtException', () => {
throw new Error('there..');
});
throw new Error('here..');The documentation shows that exit code 7 means “internal exception handler runtime failure”, matching the observed behavior.
Deprecated API Warnings
Node.js marks deprecated APIs with util.deprecate. Example:
const util = require('util');
exports.mergeData = util.deprecate(() => {
// old implementation
}, 'mergeData() is deprecated. Use merge() instead.');Flags to control deprecation warnings: --no-deprecations – suppresses deprecation warnings (not recommended). --trace-deprecation – adds a stack trace to the warning. --throw-deprecations – turns deprecation warnings into errors.
Detecting Synchronous I/O Operations
Because Node.js runs on a single thread, long‑running synchronous I/O blocks the event loop. Use the --trace-sync-io flag to detect such calls.
const { readFileSync } = require('fs');
setImmediate(() => readFileSync(__filename));Unhandled Promise Rejection
Example code that triggers an unhandled rejection warning:
const p = new Promise((resolve, reject) => {
// throw new Error(111); // equivalent to next line
reject(new Error(111));
}); (node:10142) UnhandledPromiseRejectionWarning: Error: 111
at /path/to/reject.js:4:10
...
(node:10142) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict`.Adding p.catch(console.log) suppresses the warning. The --unhandled-rejections flag supports several modes:
throw : emits an unhandledRejection event; if unhandled, it becomes an uncaught error (default).
strict : throws the error immediately.
warn : always prints a warning.
warn-with-error-code : warns and sets exit code 1 if unhandled.
none : silences all warnings.
Running with --unhandled-rejections=strict causes the process to crash:
$ node --unhandled-rejections=strict reject.js
/path/to/reject.js:4
reject(new Error(111));
^
Error: 111
at /path/to/reject.js:4:10
...This “let it crash” approach helps surface hidden errors that could otherwise lead to memory leaks.
Node Underground
No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
