This error occurs when a callback function doesn't follow Node.js's error-first callback convention, which requires the first parameter to be reserved for an error object. Node.js expects callbacks to accept an error as the first argument (null if no error), followed by result data.
This error indicates that a callback function was defined or invoked without including an error parameter as its first argument, violating Node.js's fundamental error-first callback pattern. In Node.js, the error-first callback convention (also called "node-style callbacks" or "errbacks") is a standard pattern where asynchronous functions pass errors and results to callback functions. The first parameter must always be reserved for an error object (null or undefined if no error occurred), and subsequent parameters contain the successful result data. This convention ensures consistent error handling across the Node.js ecosystem and allows developers to handle errors in a predictable way. When a callback doesn't include this error parameter, it breaks compatibility with Node.js APIs and libraries that expect this pattern.
Ensure your callback function includes an error parameter (commonly named err or error) as the first argument:
// Before: Missing error parameter
fs.readFile('file.txt', 'utf8', (data) => {
console.log(data);
});
// After: Correct error-first callback
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log(data);
});Always include the error parameter even if you don't plan to use it, as Node.js APIs expect this signature.
Always check if the error parameter is truthy before accessing result parameters:
function processData(callback) {
// Correct: Check error first
someAsyncOperation((err, result) => {
if (err) {
return callback(err); // Propagate error
}
// Only use result if no error
const processed = transform(result);
callback(null, processed); // null indicates success
});
}
// Incorrect: No error check
function badProcessData(callback) {
someAsyncOperation((err, result) => {
const processed = transform(result); // May crash if err exists
callback(processed); // Missing error parameter
});
}When writing functions that accept callbacks, always pass errors as the first argument:
function readUserData(userId, callback) {
database.query('SELECT * FROM users WHERE id = ?', [userId], (err, rows) => {
if (err) {
// Pass error as first argument
return callback(new Error(`Failed to read user: ${err.message}`));
}
if (rows.length === 0) {
// Pass error for not found
return callback(new Error('User not found'));
}
// Pass null for error, data as second argument
callback(null, rows[0]);
});
}
// Usage
readUserData(123, (err, user) => {
if (err) {
console.error(err);
return;
}
console.log('User:', user);
});If you have Promise-based code that needs to work with callback-expecting APIs, use Node.js's built-in util.callbackify:
const util = require('util');
// Promise-based function
async function fetchData(url) {
const response = await fetch(url);
return response.json();
}
// Convert to callback-style (automatically follows error-first convention)
const fetchDataCallback = util.callbackify(fetchData);
// Now usable with callbacks
fetchDataCallback('https://api.example.com/data', (err, data) => {
if (err) {
console.error('Fetch failed:', err);
return;
}
console.log('Data:', data);
});This ensures the error-first convention is properly followed without manual conversion.
When defining functions that accept callbacks, document the expected signature:
/**
* Processes a file asynchronously
* @param {string} filePath - Path to the file
* @param {Function} callback - Callback with signature (err, result)
* @callback callback
* @param {Error|null} err - Error object or null if successful
* @param {string} result - Processed file content
*/
function processFile(filePath, callback) {
// Type check the callback
if (typeof callback !== 'function') {
throw new TypeError('Callback must be a function');
}
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
return callback(err);
}
try {
const processed = data.toUpperCase();
callback(null, processed);
} catch (processingError) {
callback(processingError);
}
});
}The error-first callback convention became the Node.js standard early in the platform's history to provide a consistent error handling pattern across all asynchronous operations. The first parameter being reserved for errors allows for easy error propagation up the call stack.
When the error parameter is null or undefined, it signals success, and subsequent parameters contain the result data. This pattern works particularly well with Node.js's single-threaded event loop model.
Modern Node.js code increasingly uses Promises and async/await, which provide cleaner error handling through try/catch blocks. However, many built-in APIs and legacy codebases still rely on callbacks. The util.promisify and util.callbackify utilities help bridge between these patterns.
Some libraries and frameworks enforce stricter callback conventions, checking parameter counts and types at runtime. This error may be thrown by such validation layers rather than Node.js core itself.
When working with TypeScript, you can use type definitions to enforce correct callback signatures:
type ErrorFirstCallback<T> = (err: Error | null, result?: T) => void;
function myAsyncFunction(callback: ErrorFirstCallback<string>) {
// TypeScript will enforce correct usage
}The error parameter should always be of type Error (or null/undefined), never a string or number. This allows for proper stack traces and error properties.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams