---
title: "Common JavaScript errors, explained"
url: https://erroneo.us/developers/javascript-errors/
description: "What \"Cannot read properties of undefined\", \"x is not a function\", CORS and JSON.parse errors mean, with exact browser messages and fixes."
type: guide
section: "Developers"
date_modified: 2026-10-22
publisher: erroneo.us
license: All rights reserved. Quote with attribution and a link.
---

# 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.

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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors) 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 type | What it means | Typical cause |
|---|---|---|
| `TypeError` | The value exists but is the wrong kind for the operation | Reading a property of `undefined`, calling a non-function, reassigning a `const` |
| `ReferenceError` | The name can't be resolved, or isn't usable yet | Typos, wrong scope, the temporal dead zone |
| `SyntaxError` | The code (or JSON) can't be parsed | Missing brackets, stray commas, HTML parsed as JSON |
| `RangeError` | A value is outside the allowed range | Runaway 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_property):

```
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); }
```

Why: [Optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function). 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_defined): 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_lexical_declaration_before_init) 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_const_assignment): 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Unexpected_token) 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/JSON_bad_parse)). 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](https://erroneo.us/developers/http-500-internal-server-error/) and [404 errors](https://erroneo.us/developers/http-404-not-found/).
- 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Too_much_recursion) 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](https://developer.mozilla.org/en-US/docs/Web/API/Window/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](https://nodejs.org/api/cli.html#--unhandled-rejectionsmode), 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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors) 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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors/CORSMissingAllowOrigin)). If you don't control the server, route the call through your own backend.

>

A failed preflight can look like a CORS error even when the real problem is a [401 or 403](https://erroneo.us/developers/http-401-vs-403/), or a [502 from a proxy](https://erroneo.us/developers/http-502-vs-504/) that strips headers. Check the Network tab for the actual status code.

## 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.

## From the Error of the Day

1. Oct 22 Developers[CORS errors: the server answered, the browser said no](https://erroneo.us/daily/cors-error/) A CORS error means the browser refused to hand your script the response. The server often answered fine. Fix it with server headers, not frontend hacks.

## 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

1. [MDN: JavaScript error reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors)
2. [MDN: TypeError: "x" is undefined / can't access property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_property)
3. [MDN: TypeError: "x" is not a function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function)
4. [MDN: ReferenceError: "x" is not defined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_defined)
5. [MDN: SyntaxError: unexpected token](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Unexpected_token)
6. [MDN: SyntaxError: JSON.parse: bad parsing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/JSON_bad_parse)
7. [MDN: InternalError: too much recursion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Too_much_recursion)
8. [MDN: TypeError: invalid assignment to const](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_const_assignment)
9. [MDN: ReferenceError: can't access lexical declaration before initialization](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_lexical_declaration_before_init)
10. [MDN: Window: unhandledrejection event](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event)
11. [MDN: CORS errors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors)
12. [MDN: Reason: CORS header 'Access-Control-Allow-Origin' missing](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors/CORSMissingAllowOrigin)
13. [MDN: Optional chaining (?.)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
14. [Node.js CLI docs: --unhandled-rejections](https://nodejs.org/api/cli.html#--unhandled-rejectionsmode)

---
Source page: https://erroneo.us/developers/javascript-errors/ · More: https://erroneo.us/llms.txt
