Troubleshooting
Scope: the errors and gotchas you'll actually hit, what they mean, and the plain fix for each. Verified against Node v24.
Runtime errors
"Cannot read properties of undefined"
const user = {};
console.log(user.address.city); // TypeError: Cannot read properties of undefined (reading 'city')
Something in the chain is undefined. Fix: optional
chaining (user?.address?.city), or check the data shape — the
API probably returned something different than you expected.
"X is not a function"
You're calling something that isn't callable — often a typo'd method name, or an import that didn't bring in what you thought:
import { readfile } from "node:fs/promises"; // lowercase f — undefined!
await readfile("x.txt"); // TypeError: readfile is not a function
Fix: check spelling and casing — imports are case-sensitive.
"Unexpected token 'export'" / "Cannot use import statement"
You're using ES module syntax in a file Node treats as
CommonJS. Fix: rename to .mjs, or add "type": "module"
to package.json. (See Modules and npm.)
"require is not defined"
The reverse: CommonJS require() in an ES module. Fix: use
import, or rename the file to .cjs.
Unhandled promise rejection → process crash
You have a promise nobody awaited or .catch()ed. Node prints
the rejection reason and exits. Fix: find the floating
promise and handle it. (See
Error Handling.)
Gotchas (runs fine, surprises you)
== vs ===
Covered in Variables and Types, but it
bites in conditions: if (input == 0) is true for "" too. Use
===, always.
await in a loop is sequential
for (const f of files) await process(f); // one at a time!
Correct when order matters; use Promise.all(files.map(process))
when it doesn't. (See
Async JavaScript.)
JSON.parse throws on bad input
JSON.parse throws synchronously — wrap it in try/catch,
especially on data from files or networks.
for...in on arrays
for...in gives indices as strings, not values — and includes
inherited keys. Use for...of for arrays.
Floating point: 0.1 + 0.2 !== 0.3
0.1 + 0.2 === 0.3; // false (!)
All JS numbers are 64-bit floats. For money, work in cents (integers) or use a decimal library.
Top-level await only in modules
await outside a function works in .mjs files (or "type":
"module" packages) but is a syntax error in CommonJS. If you see
"await is only valid in async functions," check the module type.
npm issues
"Cannot find module 'x'"
The package isn't installed (or you're in the wrong folder).
Fix: npm install in the project root. Check package.json
lists it.
node_modules weirdness after switching branches
Delete and reinstall — it's a cache:
rm -rf node_modules package-lock.json
npm install
(Keep package-lock.json in git normally; deleting it here is
the "nuke it from orbit" fix for a corrupted tree.)
Permission errors on npm install -g
Never sudo npm install -g — it creates root-owned files in
your npm cache. Fix: configure npm to use a user-owned
directory, or better, install global tools via your version
manager (fnm/nvm handle this cleanly).
The meta-rule
Read the first line of the stack trace — it's usually the real
error; the rest is the call path that got you there. And when an
error message quotes your code back at you with a ^ caret,
look exactly where it points.
Check
- [ ] You can fix the import/export vs require/module mismatch both directions
- [ ] You know what an unhandled rejection looks like
- [ ] You can explain why
0.1 + 0.2 !== 0.3
Next: Glossary.