Developers

Common JavaScript errors, explained

The ten error messages developers paste into search bars most often: what each one means, how Chrome, Firefox and Safari word it differently, a minimal reproduction, and the fix.

InteractivePaste your exact error and jump to the right fix. Open Error Message Decoder →

JavaScript error messages are short, and they don't always point at the line that caused the problem. They also differ between engines: Chrome, Edge and Node.js run V8, Firefox runs SpiderMonkey, and Safari runs JavaScriptCore, and each words the same failure its own way. The MDN JavaScript error reference lists the variants side by side, and we use it as the main source below. The V8 messages on this page were also checked by running the snippets in Node.js 24.

Every message has three parts: the error type (TypeError, ReferenceError, SyntaxError, RangeError), the message, and a stack trace. Read the type first, because it narrows the cause before you read anything else.

Error typeWhat it meansTypical cause
TypeErrorThe value exists but is the wrong kind for the operationReading a property of undefined, calling a non-function, reassigning a const
ReferenceErrorThe name can't be resolved, or isn't usable yetTypos, wrong scope, the temporal dead zone
SyntaxErrorThe code (or JSON) can't be parsedMissing brackets, stray commas, HTML parsed as JSON
RangeErrorA value is outside the allowed rangeRunaway recursion, invalid array length

TypeError: Cannot read properties of undefined (reading 'x')#

This is the most common JavaScript error by a wide margin. You read a property, or called a method, on something that turned out to be undefined or null. MDN lists the engine variants:

V8 (Chrome/Edge/Node):  TypeError: Cannot read properties of undefined (reading 'x')
Firefox:                TypeError: "x" is undefined
Safari:                 TypeError: undefined is not an object (evaluating 'obj.x')

The property name in the message is the one you were trying to read. The thing that was undefined is whatever sits to its left. In user.profile.name failing with (reading 'name'), the culprit is user.profile.

const res = await fetch("/api/user");
const user = await res.json();
console.log(user.profile.name); // profile missing for some users
const name = user?.profile?.name ?? "Anonymous";
// or check explicitly before you use it
if (user && user.profile) { render(user.profile.name); }

Optional chaining returns undefined instead of throwing. Use it when the data really can be missing. If it should never be missing, find out why it is.

Common sources: an API response shaped differently than you assumed, array access past the end (items[items.length]), DOM queries that matched nothing (document.querySelector returns null), and reading state before an asynchronous load finishes.

TypeError: x is not a function#

All three engines use essentially the same wording here, according to MDN. You put () after something that isn't callable.

TypeError: o.foo is not a function
  • Typos in method names. MDN's own example is document.getElementByID. The method is getElementById.
  • The method exists on a different type. obj.map(...) on a plain object fails because map lives on arrays. Use Object.entries(obj).map(...).
  • A property shadows the method. Assigning this.name = "Ralph" replaces a prototype method also called name.
  • Wrong import style. Importing a default export with named-import braces, or the reverse, gives you undefined.
  • Implicit multiplication. 2(3 + 5) is parsed as calling the number 2.

ReferenceError: x is not defined#

V8 / Firefox:  ReferenceError: x is not defined
Safari:        ReferenceError: Can't find variable: x

The name was never declared anywhere the current code can see. MDN gives three usual causes: a typo or missing declaration, a variable declared inside a function and used outside it, or a library (such as jQuery's $) used before its <script> tag loaded. Check the spelling and capitalisation first, because identifiers are case-sensitive.

ReferenceError: Cannot access 'x' before initialization#

V8:       ReferenceError: Cannot access 'x' before initialization
Firefox:  ReferenceError: can't access lexical declaration 'x' before initialization
Safari:   ReferenceError: Cannot access uninitialized variable.

This is the temporal dead zone. let, const and class declarations are hoisted, but they stay uninitialized until the declaration line runs, and touching them before then throws. var doesn't throw. It quietly gives you undefined, which is often worse. MDN points out that what counts is execution order, not source order, and that circular ES module imports are a classic trigger.

function total() {
  console.log(tax);   // ReferenceError
  const tax = 0.08;
}
function total() {
  const tax = 0.08;
  console.log(tax);   // 0.08
}

TypeError: Assignment to constant variable#

V8:       TypeError: Assignment to constant variable.
Firefox:  TypeError: invalid assignment to const 'x'
Safari:   TypeError: Attempted to assign to readonly property.

const makes the binding read-only, not the value. Changing obj.foo on a const obj is fine. Writing obj = {} is not. MDN's fixes: switch to let if the value really must change, or use a new name. A frequent version of this bug is for (const i = 0; i < n; i++), because the i++ reassigns the constant.

SyntaxError: Unexpected token#

V8:       SyntaxError: Unexpected token ';'
          SyntaxError: Unexpected identifier 'x'
Firefox:  SyntaxError: expected expression, got ';'

The parser hit a character it couldn't fit into valid grammar. MDN's tip is the useful part here: the reported position is often after the real mistake, so look at the lines above it. Usual causes are a missing closing bracket, a trailing comma inside an expression, or if (a) || (b) with the parentheses in the wrong place. If the "token" is < in a .js file, your server probably returned an HTML page where a script was expected.

SyntaxError: Unexpected end of JSON input (and other JSON.parse errors)#

JSON.parse and response.json() throw a SyntaxError when the text isn't strict JSON. These are the V8 messages we reproduced in Node.js:

JSON.parse("")                → Unexpected end of JSON input
JSON.parse("<!DOCTYPE html>") → Unexpected token '<', "<!DOCTYPE html>" is not valid JSON
JSON.parse({})                → "[object Object]" is not valid JSON
JSON.parse('{"a":1,}')        → Expected double-quoted property name in JSON at position 7

Firefox prefixes its versions with JSON.parse:, for example unexpected end of data or expected property name or '}' (MDN). The usual causes:

  • An empty body, for example from a 204 No Content or a failed request, parsed as JSON.
  • An HTML error page parsed as JSON. See 500 errors and 404 errors.
  • Trailing commas, single quotes, unquoted keys or comments. These are valid in JavaScript object literals but not in JSON.
  • Passing an object that's already parsed back into JSON.parse.
const res = await fetch(url);
const text = await res.text();
if (!res.ok) throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
const data = text ? JSON.parse(text) : null;

RangeError: Maximum call stack size exceeded#

V8 / Safari:  RangeError: Maximum call stack size exceeded
Firefox:      InternalError: too much recursion

Too many function calls were nested at once. MDN lists a recursive function with no base case, a base case that's never reached, and a sneakier one: a setter that assigns to its own property and so calls itself forever.

class Person {
  set name(v) { this.name = v; } // calls the setter again
}
class Person {
  set name(v) { this._name = v; }
  get name()  { return this._name; }
}

Very deep but finite recursion, such as walking a large tree, can also hit the limit. Rewrite it as a loop with an explicit stack.

Uncaught (in promise)#

When a promise rejects and nothing handles the rejection, browsers fire an unhandledrejection event and, if nothing cancels it, log an error to the console. Chrome writes the reason after Uncaught (in promise). In Node.js, the --unhandled-rejections default became throw in v15.0.0, so an unhandled rejection with no handler is raised as an uncaught exception and stops the process.

button.onclick = () => {
  saveDraft(); // returns a promise that may reject
};
button.onclick = async () => {
  try { await saveDraft(); }
  catch (err) { showToast("Couldn't save, try again"); }
};

Access to fetch has been blocked by CORS policy#

Chrome:   Access to fetch at 'https://api.example.com/' from origin 'https://app.example.com'
          has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present...
Firefox:  Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote
          resource at https://api.example.com/. (Reason: CORS header 'Access-Control-Allow-Origin' missing)

CORS errors confuse people because the request often did reach the server. The browser just refused to let your script read the response. MDN notes that for security reasons the details "are not available to JavaScript code": your catch only sees a generic network failure, and the real reason is in the console.

The fix is on the server. Send Access-Control-Allow-Origin with your origin, or with * for truly public, credential-free APIs. When cookies or multiple origins are involved, echo back an allowed Origin and add Vary: Origin (MDN). If you don't control the server, route the call through your own backend.

A debugging checklist#

  • Read the error type first, then the message, then the first stack frame in your own code.
  • Log the value just before the failing line. Most TypeErrors come down to "this wasn't what I thought it was".
  • For network-related errors, open the Network tab and check the status code and response body before blaming your parsing code.
  • Reproduce it in the smallest possible snippet. If the error disappears, the cause is in what you removed.

Frequently asked questions

Why does Chrome say "Cannot read properties of undefined" when Safari says "undefined is not an object"?

They are the same error. Each JavaScript engine words its messages differently: V8 (Chrome, Edge, Node.js) says Cannot read properties of undefined (reading 'x'), Safari's JavaScriptCore says undefined is not an object (evaluating 'obj.x'), and Firefox says "x" is undefined. All three are a TypeError raised because you tried to read a property of undefined.

Can I fix a CORS error in my front-end code?

Usually not. The browser blocks the response because the server did not send an Access-Control-Allow-Origin header that allows your origin. The fix belongs on the server (or a proxy you control). Setting mode: "no-cors" only makes the response opaque, so your script still cannot read it.

What does "Uncaught (in promise)" mean?

A promise was rejected and nothing handled the rejection: no .catch(), no second argument to .then(), and no try/catch around an await. Browsers report it in the console. In Node.js 15 and later the default is to treat it as an uncaught exception, which ends the process.

Why does JSON.parse fail with "Unexpected token <"?

The text you parsed starts with <, which almost always means the server sent back an HTML page (an error page, a login redirect, or a 404) instead of JSON. Log the raw response text and check the HTTP status and Content-Type header before parsing.

Is "Maximum call stack size exceeded" always caused by recursion?

Nearly always. It means too many function calls were nested at once, usually because a recursive function has no reachable base case. A common hidden version is a setter that assigns to its own property, which calls the setter again forever.

Sources

Every factual claim on this page links to one of these. Checked October 2026. See how we source.

  1. MDN: JavaScript error reference developer.mozilla.org
  2. MDN: TypeError: "x" is undefined / can't access property developer.mozilla.org
  3. MDN: TypeError: "x" is not a function developer.mozilla.org
  4. MDN: ReferenceError: "x" is not defined developer.mozilla.org
  5. MDN: SyntaxError: unexpected token developer.mozilla.org
  6. MDN: SyntaxError: JSON.parse: bad parsing developer.mozilla.org
  7. MDN: InternalError: too much recursion developer.mozilla.org
  8. MDN: TypeError: invalid assignment to const developer.mozilla.org
  9. MDN: ReferenceError: can't access lexical declaration before initialization developer.mozilla.org
  10. MDN: Window: unhandledrejection event developer.mozilla.org
  11. MDN: CORS errors developer.mozilla.org
  12. MDN: Reason: CORS header 'Access-Control-Allow-Origin' missing developer.mozilla.org
  13. MDN: Optional chaining (?.) developer.mozilla.org
  14. Node.js CLI docs: --unhandled-rejections nodejs.org