Use it like a map, not a textbook.
Assumption: you’re on macOS and you’re using Google tools (usually Chrome + Chrome DevTools). The ideas still apply elsewhere.
Before we name specific errors, it helps to decide what kind of problem you’re in.
A quick if/then triage (30 seconds)
- If the error mentions “SyntaxError”, then the code didn’t parse. Look for a missing bracket/quote, a trailing comma in the wrong place, or a feature your environment doesn’t support.
- If the error mentions “ReferenceError”, then something is not defined in the current scope (variable, function, import).
- If the error mentions “TypeError”, then something exists, but it’s the wrong type for what you tried to do (calling a non-function, reading a property of undefined, etc.).
- If the error mentions “Uncaught (in promise)”, then an async operation rejected and nobody handled it with catch (or try/catch around await).
- If things “work” but behavior is wrong, then it may be a logic bug, stale data, or an event/order issue—use breakpoints and logs instead of staring at the message.
When you’re stuck: open DevTools → Console, click the error, and jump to the exact line. Then scroll up the stack trace to find the first line that belongs to your code (not a library).
Now the glossary.
“SyntaxError: Unexpected token …”
Plain English: JavaScript can’t even start running this file, because the text doesn’t form valid JS.
If/then: If you see “Unexpected token”, then check the characters just before the token (often the real issue is earlier on the line).
- If it’s “Unexpected token <”, then you might be loading HTML where JS is expected (wrong URL, server error page, or missing build output).
- If it’s “Unexpected end of input”, then something didn’t close (a brace, bracket, parenthesis, or quote).
Example (missing brace):
Bad: if (ok) { console.log('hi')
Better: if (ok) { console.log('hi') }
Mac + Google tip: In Chrome DevTools, pretty-print (the {} button) can make a missing bracket easier to spot in bundled/minified code.
Syntax errors are annoying, but at least they’re honest: you fix the text, and you move on.
“ReferenceError: X is not defined”
Plain English: You used a name (variable/function/class) that doesn’t exist in the current scope.
If/then: If “is not defined”, then ask: “Should this name exist here?”
- If it’s a typo, then fix spelling/case. JS is case-sensitive.
- If it’s defined in another file, then you probably forgot to import/export it (or the import path is wrong).
- If it’s inside a block, then you might be trying to use a let/const variable outside where it was declared.
Example (block scope surprise):
Bad: if (loggedIn) { const user = getUser(); } console.log(user)
Better: let user; if (loggedIn) { user = getUser(); } console.log(user)
Google-tool habit: In the Console, run typeof X. If it returns "undefined", you’re truly missing the binding in that scope.
ReferenceError is often a “wiring” problem: imports, script order, or scope boundaries.
“TypeError: Cannot read properties of undefined (reading '…')”
Plain English: You expected an object, but you got undefined (or null), and you tried to read a property on it.
If/then: If it says “Cannot read properties of undefined”, then find where that value is coming from and why it’s missing.
- If the value comes from an API response, then the shape may differ from what you assumed (missing field, different nesting, empty list).
- If it comes from the DOM, then your selector didn’t find anything (element not on the page yet, wrong selector, different route/layout).
- If it comes from state, then it may not be initialized yet (first render, async load, race conditions).
Example (DOM selector returns null):
Bad: document.querySelector('#save').addEventListener('click', save)
Better: const btn = document.querySelector('#save'); if (btn) btn.addEventListener('click', save)
DevTools move: Put a breakpoint on the failing line, then hover the variable (or inspect it in the Scope panel). Don’t guess which part is undefined.
If you keep hitting this one, you may need to design for “not loaded yet” as a normal state, not an exception.
“TypeError: X is not a function”
Plain English: You tried to call something like a function (X()), but the value in X isn’t callable.
If/then: If “not a function”, then check what X actually is at runtime.
- If you imported it, then you may have mixed up default vs named exports.
- If it’s a method, then the object might not be what you think (lost this, wrong instance, overwritten property).
- If it’s optional, then you need a guard before calling it.
Example (default vs named import mismatch):
Bad: import doThing from './utils.js'; doThing() (but utils exports export const doThing = ...)
Better: import { doThing } from './utils.js'; doThing()
Console check: Log the value right before calling: console.log('X is', X). If it’s an object or undefined, you’ve found the category of mistake.
This error is less about the call site and more about how the value got there.
“Uncaught (in promise) …”
Plain English: A Promise rejected (an error happened in async code) and nothing handled it.
If/then: If you see “in promise”, then follow the chain to where it should be caught.
- If you used await, then wrap it in try/catch.
- If you used .then(), then add .catch() (or return the Promise so a caller can catch it).
- If this started after a refactor, then a missing return inside a promise chain can silently break error propagation.
Example (await without try/catch):
Better: try { const res = await fetch(url); } catch (err) { console.error(err); }
Mac + Google tip: In DevTools, enable “Pause on exceptions” (and consider “Pause on caught exceptions” temporarily) to stop at the real origin before it bubbles into “Uncaught”.
Async errors often look far away from where they started. The stack trace is your friend here.
“CORS policy … has been blocked”
Plain English: The browser is protecting the user. Your page is trying to request a resource from another origin, and that server didn’t allow it.
If/then: If you see CORS errors, then don’t “fight the browser” in frontend code—fix the server headers or the architecture.
- If you control the API, then configure CORS to allow your site origin (and the specific methods/headers you need).
- If you don’t control the API, then use a backend proxy you control, or switch to a supported integration method.
- If it works in curl/Postman but not in the browser, that’s a classic sign it’s truly CORS (not “the API is down”).
DevTools clue: Look at the Network tab for the preflight OPTIONS request. If that fails, the real request won’t be allowed.
CORS is not a JavaScript “bug” so much as a permission mismatch between origins.
Takeaway: a small decision framework you can reuse
If it won’t parse, it’s SyntaxError: fix characters and compatibility.
If a name doesn’t exist, it’s ReferenceError: fix scope/import/script order.
If a value exists but can’t be used that way, it’s TypeError: inspect runtime values and assumptions.
If it’s async and says “promise”, handle rejection where it should be handled.
If it’s cross-site, think CORS/permissions and change the server or the plan.