ScreenshotNeo

BlogGuides

Common JavaScript Errors and How to Fix Them

How do I fix common JavaScript errors? Learn to read the message, trace the cause, and fix syntax, reference, type, and range errors.

By the ScreenshotNeo team4 October 20269 min read

How do I fix common JavaScript errors? Start with the error class, message, and source location in the console. Then inspect the nearby code and the values it uses, reproduce the failure with a small input, and fix one cause at a time. A message is a clue, not a complete diagnosis: the marked line can be downstream from the original mistake, and wording varies by browser.

Most beginner-facing errors fall into four groups: SyntaxError means the parser cannot understand the code; ReferenceError means a name is unavailable or not initialized; TypeError means an operation does not fit the value; and RangeError means a value is outside an operation’s permitted range.

1. First identify when the error happens

JavaScript errors are easier to diagnose when you first ask whether the code failed before it could run or during execution.

Error When it happens What to check first
SyntaxError Parsing: JavaScript cannot interpret the source. Punctuation, quotes, delimiters, and whether the construct is valid in context.
ReferenceError Execution: a name cannot be resolved, or a lexical binding is read before initialization. Spelling, declaration, scope, and initialization order.
TypeError Execution: an operation was attempted with an unsuitable value. The value’s actual type and shape at the failing operation.
RangeError Execution: a constrained value is outside the allowed range or set. Input bounds and the operation’s accepted domain.

JavaScript error objects have a name and a message. The console’s source location and stack information can help you find the failing path, but neither the wording nor the highlighted line necessarily explains the root cause. MDN describes SyntaxError as an error raised when trying to interpret syntactically invalid code (MDN: SyntaxError).

2. Use a repeatable debugging workflow

  1. Read the whole console entry. Note the error class, full message, source file and line, and stack trace if present. Browser engines can phrase similar errors differently.
  2. Open the cited location. Inspect the whole expression and a few lines around it. A parser may point at the token where it noticed trouble even though a missing quote or delimiter came earlier.
  3. Check the inputs immediately before the operation. For a property access, inspect the object. For a function call, check that the value is callable. For a number or length, check its bounds.
  4. Reproduce with the smallest relevant case. Reduce the input and remove unrelated code until the same failure remains.
  5. Step through or log the path. Use browser developer tools’ breakpoints and debugger, or targeted console.log statements, to see which branch ran and what values were present.
  6. Change one cause, then repeat the reproduction. Confirm the message is gone and the intended behavior still occurs.
  7. Run a linter. ESLint and similar tools can catch invalid syntax and likely mistakes during development. A linter complements runtime debugging; it does not prove that application behavior is correct.
// A compact way to inspect an unexpected value before using it:
function inspectItems(value) {
  console.log({ value, type: typeof value, isArray: Array.isArray(value) });

  if (!Array.isArray(value)) {
    throw new TypeError("Expected an array of items");
  }

  return value.map((item) => item.name);
}

Prefer inspecting the value at the point of failure to guessing from what an API or caller is supposed to return.

3. Fix SyntaxError and unexpected-token errors

A SyntaxError means the parser cannot interpret the source. Typical causes are a typo, a missing delimiter, a malformed expression, or an invalid character. For SyntaxError: Unexpected token, inspect the expression immediately before the reported token as well as the token itself.

Check the surrounding punctuation

  • Match every opening (, [, and { with its closing partner.
  • Check commas and colons in arrays, objects, function arguments, and declarations.
  • Check that conditions, loops, calls, and blocks use punctuation valid for that construct.
  • Check whether the reported syntax is allowed in its location; valid syntax in one context may be invalid in another.
// Broken: the condition is missing its closing parenthesis.
if (items.length > 0 {
  console.log(items[0]);
}

// Fixed
if (items.length > 0) {
  console.log(items[0]);
}

Messages about a missing ), ], }, or : usually call for checking the nearby call, condition, array, object, or block. Do not change the marked token blindly: the parser may only have lost track of the expression there.

Check quotes and lookalike characters

Code punctuation must be ordinary JavaScript punctuation. Curly quotation marks and typographic dashes can look like valid characters but are not interchangeable with straight quotes and the hyphen-minus in code. An unescaped line break or missing quote can also leave a string malformed.

// Broken: typographic quotes are not JavaScript string delimiters.
const greeting = “Hello”;

// Fixed
const greeting = "Hello";

If a suspicious character is intended as text, put it inside a correctly delimited string. When a parser flags a later token, look backward for the first unmatched quote, bracket, or brace.

4. Fix ReferenceError: a name is not defined

ReferenceError: x is not defined usually means the identifier x is not available in the current scope. It can also arise when a lexical declaration is accessed before initialization.

// Broken: the declared name and the name used below do not match.
const customerName = "Ari";
console.log(custmerName);

// Fixed
const customerName = "Ari";
console.log(customerName);

Check these in order:

  1. Compare spelling and capitalization. JavaScript identifiers are case-sensitive.
  2. Find the declaration and confirm it runs before the read.
  3. Check scope: a variable declared inside a function or block may not be available outside it.
  4. For let and const, check whether code reads the binding before its declaration has initialized it.
  5. In browser code, confirm the script or module that defines the name is loaded in the expected scope.

Do not silence the error by declaring a second variable until you know whether the intended value is missing, misspelled, out of scope, or read too early.

5. Fix TypeError by checking the value and operation

A TypeError usually means an operation is incompatible with the value it received. The same line of code can work for one input and fail for another, so inspect the runtime value and its shape.

Calling a value that is not a function

const settings = { onSave: true };

// Broken: onSave is a boolean, not a function.
settings.onSave();

// Fixed: call it only when the property is a function.
if (typeof settings.onSave === "function") {
  settings.onSave();
}

Reading a property from null or undefined

const user = null;

// Broken: null has no name property.
console.log(user.name);

// Fixed when absence is allowed.
console.log(user?.name ?? "Unknown user");

Optional chaining is appropriate when the value may legitimately be absent. If absence indicates broken application state, validate it and report the problem instead of silently substituting a default.

Iterating a value that is not iterable

function renderNames(users) {
  if (!Array.isArray(users)) {
    throw new TypeError("Expected users to be an array");
  }

  return users.map((user) => user.name);
}

Typical clues include errors about calling a non-function, an object not being iterable, or reading a property of null or undefined. Check whether a response has the expected shape; for example, a request may return an error object rather than the array the code assumes.

6. Fix RangeError by validating bounds

A RangeError indicates that a value exceeds an operation’s accepted range or set. Examples include an invalid array length, unsupported precision for number formatting, or an unsupported string normalization form.

function makeList(length) {
  if (!Number.isInteger(length) || length < 0) {
    throw new RangeError("length must be a non-negative integer");
  }

  return new Array(length);
}

console.log(makeList(3));

Validate values at the boundary where they enter your code, and follow the specific operation’s documented limits. Do not assume every numeric argument accepts decimals, negative values, or arbitrarily large values.

7. Troubleshooting checklist

Console clue Likely cause First fix to try
SyntaxError: Unexpected token Earlier punctuation is missing or malformed, or the construct is invalid in context. Inspect the expression before the token; match delimiters and check separators and quotes.
Missing ), ], }, or : A nearby call, condition, array, object, or block is incomplete. Compare opening and closing marks and inspect the entire construct.
Illegal or unexpected character A lookalike punctuation character, broken string, or invalid character in code. Replace typographic punctuation with ordinary syntax; check string boundaries.
ReferenceError for a name Misspelling, wrong capitalization, unavailable scope, or early read. Find the declaration and trace when and where it is initialized.
TypeError on a property or call The value is nullish or has a different type or shape than expected. Inspect the value immediately before use and validate the contract.
TypeError while iterating The input is not iterable or is not the expected array. Check the input with Array.isArray or the relevant iterable contract.
RangeError An argument is outside the operation’s valid domain. Validate the input type and bounds against that operation’s documentation.

8. Capture a reproducible browser failure

When an error depends on a particular page state, viewport, or consent dialog, a screenshot can preserve the visual context alongside the console message. You can capture the page manually with browser developer tools, or use an API after identifying a reproducible URL and state.

9. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. A single GET request can return a PNG, JPEG, WebP, or PDF; its API documentation covers the available parameters. For a repeatable page capture, use the request below.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res); // In Node.js, use: await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))

The Node.js example uses the built-in fetch; to save the response with Node’s file system module, use this complete version:

import { writeFile } from 'node:fs/promises';

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Before capture, ScreenshotNeo accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000, and every feature is available on every plan.

Create a free ScreenshotNeo account and capture 1,000 screenshots a month with no card.

10. FAQ

Does every error message identify the root cause?

No. Treat the class, message, line, and stack as clues. The parser or runtime may report where it detected the problem, while the mistake began earlier.

Why does the same error look different in another browser?

Error wording is not identical across JavaScript engines. Diagnose from the error class, surrounding code, and runtime values rather than relying on an exact message string.

Can a linter fix runtime errors?

A linter catches syntax problems and likely mistakes before or during development, but it cannot know every value a program will receive. Keep runtime checks and debugging in place.

Should I use optional chaining to fix every property error?

No. Use it when a missing value is expected and has a meaningful fallback. If the value must exist, validate the assumption and fix the source of the missing data.

What should I include when asking someone to diagnose an error?

Share the exact message and stack, the smallest relevant code sample, the input that reproduces it, and the values’ expected and actual shapes. Remove credentials and private data first.