What is the error-first callback pattern in Node.js?
Learn what the error-first callback pattern is in Node.js, why err comes first, and how to modernize it with util.promisify for cleaner async code.
Expected Interview Answer
An error-first callback is a Node.js convention where every asynchronous function's callback receives an error object (or null) as its first argument, followed by the result data, so the caller must check for failure before using the result.
Because early Node had no native promises, its core APIs (fs, http, dns, child_process) standardized on a single calling convention: function(err, result) { ... }. If the operation succeeded, err is null and result holds the data; if it failed, err is an Error instance describing what went wrong and result is typically undefined. This convention lets developers write consistent, predictable async code across the entire ecosystem, since every callback-based API behaves the same way. The pattern's biggest pitfall is callback hell: nesting multiple error-first callbacks creates deeply indented, hard-to-follow code, and forgetting to check err in even one callback lets failures silently propagate as if they succeeded. Modern Node still exposes many error-first callback APIs for backward compatibility, but util.promisify() or the fs.promises / dns.promises variants convert them into promise-returning functions, making them usable with async/await while still following the underlying convention internally.
- Uniform async error handling across the entire Node core API surface
- No exceptions to catch across an async boundary, since errors travel through the callback
- Easily convertible to promises via util.promisify for use with async/await
AI Mentor Explanation
An error-first callback is like a runner reporting back to the dugout after every single: the first word out is always whether they're out or safe, and only then do they add the score update. A Node function calls back the same way, passing an error argument first so the caller checks failure before ever touching the actual result data.
Step-by-Step Explanation
Step 1
Define the signature
Write callback(err, result) as the standard shape for any async function you expose.
Step 2
Check err first
Always test if (err) { return callback(err); } before touching result.
Step 3
Call with null on success
Invoke callback(null, result) when the operation completes without failure.
Step 4
Avoid double-calling
Ensure the callback fires exactly once, using return statements to prevent invoking it twice.
Step 5
Promisify when convenient
Wrap legacy error-first functions with util.promisify to use them with async/await.
What Interviewer Expects
- Recognition of the function(err, result) signature as a Node core convention
- Understanding of why unchecked errors silently swallow failures
- Awareness of util.promisify and *.promises APIs as modernization paths
- Concern for avoiding callback hell in deeply nested async code
Common Mistakes
- Forgetting to check the err argument before using result
- Calling the callback more than once inside the same async operation
- Throwing inside a callback instead of passing the error as the first argument
Best Answer (HR Friendly)
“The error-first callback pattern is Node's standard way of reporting whether an async operation succeeded or failed, ensuring the developer always checks for errors before trusting the result.”
Code Example
const fs = require('fs');
const { promisify } = require('util');
fs.readFile('data.txt', 'utf8', (err, data) => {
if (err) return console.error('Read failed:', err.message);
console.log('Content:', data);
});
const readFileAsync = promisify(fs.readFile);
readFileAsync('data.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err.message));Follow-up Questions
- Why do Node core APIs use the (err, result) callback signature instead of throwing?
- What is callback hell, and how does the error-first pattern contribute to it?
- How does util.promisify convert an error-first function into a promise-based one?
- What happens if a callback is invoked more than once in an async operation?
- How do fs.promises and dns.promises relate to their error-first counterparts?
MCQ Practice
1. In the error-first callback convention, what is the first argument?
By convention the first argument is always the error (or null), followed by the result.
2. What utility converts an error-first function into one returning a promise?
util.promisify wraps error-first functions so they return promises for use with async/await.
3. What is a common bug when working with error-first callbacks?
Skipping the err check lets failures silently pass through as if they succeeded.
Flash Cards
What is the error-first callback pattern? — A convention where async callbacks receive (err, result), with err checked before using result.
What does util.promisify do? — Converts an error-first callback function into one that returns a promise.
What is callback hell? — Deeply nested error-first callbacks that make async code hard to read and maintain.
What happens if err is not checked? — A failed operation's error is silently ignored, and the code may proceed as if it succeeded.