This error occurs when code attempts to use a non-function value as a function, or when a function is expected to return a promise but returns a non-thenable value instead. It commonly happens in promise chains, async/await operations, or when working with promise constructors.
This TypeError indicates one of two distinct problems in your Node.js code. First, it may mean you're trying to call something as a function that isn't actually a functionโlike calling `myVariable()` when `myVariable` is a string, number, or undefined. Second, and more specific to promises, it occurs when code expects a promise or thenable object but receives a value that doesn't implement the promise interface. The error is particularly common in promise-based code where functions are expected to return promises but instead return regular values, or where promise handlers are misconfigured. The JavaScript runtime enforces strict typing for promise operations, and this error acts as a guard against improper promise usage. Understanding this error requires recognizing that in JavaScript's promise ecosystem, a "thenable" is any object with a `.then()` method. When the runtime encounters a value that should be thenable but isn't, or when a supposed function isn't callable, it throws this error to prevent further execution problems.
Add console.log or debugger statements to check what the value actually is before attempting to use it:
// Check if value is actually a function
console.log('Type:', typeof myValue);
console.log('Value:', myValue);
// For promises, check if it's thenable
console.log('Is thenable:', myValue && typeof myValue.then === 'function');This will help you identify whether you're dealing with a function-calling error or a promise-related error.
If you're creating a promise, ensure you pass an executor function to the constructor:
// Wrong - missing executor function
const myPromise = new Promise();
// Correct - provide executor function
const myPromise = new Promise((resolve, reject) => {
// Your async operation here
setTimeout(() => {
resolve('Success!');
}, 1000);
});The executor function must accept resolve and reject parameters.
When chaining promises, make sure each handler returns a promise if the next .then() expects one:
// Wrong - returning non-promise in chain expecting promises
fetchData()
.then(data => {
return data.id; // Returns a number, not a promise
})
.then(fetchUserById); // Expects a promise, gets a number
// Correct - explicitly return a promise
fetchData()
.then(data => {
return Promise.resolve(data.id);
})
.then(id => fetchUserById(id));
// Better - let the function handle the promise
fetchData()
.then(data => fetchUserById(data.id))
.then(user => console.log(user));Add semicolons after require() statements to prevent JavaScript from misinterpreting your code:
// Wrong - missing semicolon can cause issues
const express = require('express')
(async () => {
// This looks like you're trying to call express as a function
})()
// Correct - add semicolon
const express = require('express');
(async () => {
// Now correctly interpreted as separate statements
})();Enable a linter like ESLint with semicolon rules to catch these automatically.
Ensure async functions properly return promises and await is used correctly:
// Wrong - async function not returning a promise
async function getData() {
const data = fetchSomething(); // Missing await
return data.value; // data might not be resolved yet
}
// Correct - properly await promise resolution
async function getData() {
const data = await fetchSomething();
return data.value;
}
// Also correct - return the promise directly
async function getData() {
return fetchSomething().then(data => data.value);
}Remember that async functions always return promises, even if you return a regular value.
Check for typos in function names that might cause undefined values to be called:
// Wrong - typo in method name
const result = myObject.proces(); // Should be 'process'
// Check if method exists before calling
if (typeof myObject.process === 'function') {
const result = myObject.process();
} else {
console.error('process method does not exist');
}Use TypeScript or JSDoc type annotations to catch these errors at development time.
Understanding Thenables: A thenable is any object that implements a .then() method according to the Promises/A+ specification. While all promises are thenables, not all thenables are promises. The JavaScript runtime will attempt to "resolve" thenables by calling their .then() method. If you're returning custom thenable objects, ensure they properly implement the interface with both onFulfilled and onRejected handler parameters.
Promise Resolution Flattening: When a promise's resolution handler returns another promise or thenable, JavaScript automatically "flattens" the chain. This means Promise.resolve(Promise.resolve(5)) eventually resolves to 5, not a nested promise. This flattening behavior can cause issues if you're expecting to receive a promise as a value rather than having it automatically resolved.
Async Function Wrapper Pattern: If you need to use await at the top level of a script (outside of an async function), you can use an immediately-invoked async function expression (IIFE): (async () => { await myAsyncOperation(); })().catch(console.error); This pattern is especially useful in Node.js scripts and is a workaround until top-level await is available in your environment.
Promise Constructor Anti-Pattern: Avoid the "promise constructor anti-pattern" where you wrap a promise-returning function in a new Promise constructor unnecessarily: new Promise((resolve) => resolve(somePromiseReturningFunction())) can simply be somePromiseReturningFunction(). The unnecessary wrapping adds overhead and can introduce bugs.
Error Handling in Promise Chains: Every promise in a chain can potentially throw an error. Use .catch() at the end of chains or wrap async/await calls in try-catch blocks. Unhandled promise rejections can crash Node.js applications in newer versions (with the --unhandled-rejections=strict flag), so proper error handling is critical for production applications.
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