Skip to content

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.