Most beginners spend their first months chasing the same handful of errors over and over. This lesson is the cheat sheet — ten error messages you'll hit repeatedly, what each one means, the buggy code that causes it, and the fix. Internalise these and the cost of writing code drops dramatically: error messages stop being scary and start being directions.
How to read a JavaScript error
Every JavaScript error gives you four pieces of information. Read them in this order:
- The error type —
TypeError,ReferenceError,SyntaxError. Tells you the category. - The message — the human description of what went wrong.
- The file and line number — where the error was thrown.
- The stack trace — the call path that led to the throw.
Don't skim the message. JavaScript tells you what's wrong — usually quite precisely. The fixes below assume you've read the message; you'll often find you already know the answer once you do.
1. "Cannot read properties of undefined (reading 'X')"
The single most common runtime error. You're accessing a property on a value that's undefined.
const user = users.find(u => u.id === 99); // 99 doesn't exist → undefined
console.log(user.name); // ❌ TypeErrorFix: check the variable first, or use optional chaining:
console.log(user?.name); // ✅ logs undefined, no crash
if (!user) return console.warn("not found"); // ✅ explicit guard
console.log(user.name);2. "X is not a function"
You're calling something that isn't a function. Two common causes:
const filter = users.filter(...); // overwrote `filter` accidentally
filter.toUpperCase(); // ❌ filter is now an array, not a function
const cb = "submit";
cb(); // ❌ "submit" isn't a functionFix: rename the shadowing variable, or fix the value before calling. A second flavour of this error: passing arguments in the wrong order, so a string lands where a callback was expected — see error 7.
3. "Unexpected token"
A SyntaxError — JavaScript can't even parse your code. Usually a missing or extra bracket, brace, comma, or quote.
const config = {
baseUrl: "https://staging.com",
timeout: 5000 // missing closing brace below
const next = 1; // ❌ Unexpected token 'const'Fix: check the line before the line in the error. Editors with bracket-matching (VS Code) make this trivial — highlight a brace and the matching one lights up. Auto-formatters (Prettier) also surface unbalanced braces immediately.
4. "X is not defined"
ReferenceError — JavaScript doesn't know the name X. Three causes, in order of probability:
console.log(usrs); // ❌ typo for users
console.log(token); // ❌ declared inside another function (scope issue)
console.log(_); // ❌ lodash not importedFix: check spelling, scope, and imports. The temporal dead zone from chapter 3 is also a possibility — using let or const before the declaration line.
5. "Cannot assign to constant variable"
You declared with const but tried to reassign:
const maxRetries = 3;
maxRetries = 5; // ❌ TypeError: Assignment to constant variableFix: use let if the value genuinely changes:
let maxRetries = 3;
maxRetries = 5; // ✅Note: const only stops reassignment. Mutating the contents of an object or array is allowed (chapter 3 covers this in detail).
6. JSON.parse errors
Almost always means the input isn't valid JSON. The most common shapes:
JSON.parse('{ "a": 1, }'); // ❌ trailing comma
JSON.parse("{ 'a': 1 }"); // ❌ single quotes
JSON.parse('{ "a": undefined }'); // ❌ undefined isn't valid JSON
JSON.parse("{ a: 1 }"); // ❌ unquoted keyFix: validate the JSON. Paste it into the JSON Formatter utility on qa.codes — it points at the precise byte that broke the parse. In code, wrap the parse in try/catch (lesson 1) so a bad fixture fails clearly instead of crashing the whole script.
7. "Callback is not a function"
You passed something that wasn't a function where a callback was expected — usually because you got the parameter order wrong:
fs.readFile("utf-8", "users.json", (err, data) => { ... });
// ^^^^^^^^ ^^^^^^^^^^^^ the args are swapped
// → TypeError: cb is not a functionFix: check the function's signature. fs.readFile(path, encoding, callback) — path first, encoding second, callback last. The error message names the parameter that wasn't a function — that's where to look in your call.
8. Off-by-one in loops
Not an explicit error, but the most common behavioural bug — looping one too many or one too few times.
for (let i = 0; i <= testCases.length; i++) { // ❌ <=, reads past the end
testCases[i].run(); // ❌ TypeError on the final iteration
}Fix: the canonical loop bound is i < array.length (strict less-than). For positional access, remember arrays are 0-indexed. Better yet, use for...of or forEach and avoid the index entirely.
9. Forgotten await
The async equivalent of "where's my data?" Without await, the variable holds a Promise object instead of the resolved value.
async function loadUsers() {
const users = fetch("/api/users").then(r => r.json()); // ❌ missing await
console.log(users.length); // ❌ users is a Promise, no .length
}Fix: add the await. When in doubt, await. (Chapter 5's full coverage.)
const users = await fetch("/api/users").then(r => r.json());
console.log(users.length); // ✅10. Comparing objects/arrays with ===
Two different object literals that look identical are still different objects — === compares references, not contents.
console.log({ a: 1 } === { a: 1 }); // ❌ false (different objects in memory)
console.log([1, 2, 3] === [1, 2, 3]); // ❌ falseFix: compare individual properties, or stringify both sides and compare the strings (good for shallow data, dangerous for objects with functions or undefined):
const a = { id: 1, role: "admin" };
const b = { id: 1, role: "admin" };
console.log(a.id === b.id && a.role === b.role); // ✅
console.log(JSON.stringify(a) === JSON.stringify(b)); // ✅ for plain dataFor deep comparison in tests, frameworks provide their own matchers — Jest's expect(a).toEqual(b), Cypress's .should("deep.equal", b). Prefer those over hand-rolled comparisons.
Practice — match each error to its fix
Match each error to the right fix
For each error, choose the most appropriate fix.
- TypeError: Cannot read properties of undefined (reading 'name')
- TypeError: filter.toUpperCase is not a function
- ReferenceError: usrs is not defined
- TypeError: Assignment to constant variable
- SyntaxError: Unexpected token in JSON at position 22
- TypeError: Cannot read properties of undefined (reading 'length') after fetch()
⚠️ Common mistakes
- Skipping past the error message.
Cannot read properties of undefined (reading 'name')tells you exactly which property and which line. Beginners often glance and re-google; reading the message saves the search. - Treating every error as a code bug. Sometimes the error is a real signal — a missing fixture, a broken API contract, a malformed network response. Don't silence the error with a
try/catchuntil you understand whether it's a bug to fix or a failure to handle. - Trusting Stack Overflow over the actual error. A 10-year-old answer to a different version of a similar error message will lead you astray. Read the message, read the stack, then search.
🎯 Practice task
Reproduce and fix each error. 25-30 minutes.
-
In your
js-for-qafolder, createbugs.js. For each of the ten errors above, write one tiny snippet that triggers it, run it, capture the exact error message and line, and add a one-line comment with the fix:// 1. TypeError: Cannot read properties of undefined const u = undefined; // console.log(u.name); // crashes — fix: u?.name console.log(u?.name); -
After all ten, you'll have a personal reference file you can consult forever.
-
Stretch: add an eleventh — pick one error you've actually hit in your own work that isn't in the list, write the snippet, capture the message, and document the fix.
That ends chapter 7. You can now write code that handles failures intentionally, debug it with structured tools instead of guesswork, and read JavaScript's error messages as the directions they actually are. The final chapter applies everything you've learned to a single, real test utility — the capstone project.