ScreenshotNeo

BlogHow-to

How to Fix JavaScript Script Errors

Find the source of a JavaScript error, inspect the values and code behind it, and fix syntax, runtime, async, or CORS failures with browser DevTools.

By the ScreenshotNeo team4 October 202610 min read

To fix a JavaScript script error, reproduce it with browser developer tools open, read the console message and source location, then inspect the code and runtime values at that point. Classify the problem as a syntax, runtime, or logic error; use logs or a debugger breakpoint to find the incorrect assumption; make a focused change; and repeat the action that originally failed.

Do not treat the wording as a complete diagnosis. Browser messages can differ, and an error may point into a bundled file rather than the source you wrote. The message, location, call stack, and values together are more useful than any one clue. MDN’s JavaScript error reference explains common error names and messages.

1. Reproduce the error and capture its context

  1. Open the page or app where the problem occurs.
  2. Open browser developer tools and select the Console. The menu names vary by browser; the tools are available in browsers such as Chrome, Firefox, and Safari.
  3. Clear old console output if needed, then repeat the exact action that triggers the problem.
  4. Record the full message, the linked filename and line or column, and the surrounding actions. If the problem happens only after a reload, with a particular account, or on a particular route, note that too.
  5. Click the source link in the console to inspect the statement and nearby code. Expand the error to see its stack trace when available.

A JavaScript error object has a name and a message. These are useful starting points, but the message may not explain the underlying cause by itself. See MDN’s error reference.

2. Identify which kind of problem you have

Kind What happens Where to start
Syntax error The JavaScript parser cannot understand the code, so that script or module cannot run as written. Check the indicated line and the lines immediately before it for missing delimiters, misspelled keywords, invalid syntax, or code being parsed under the wrong script/module settings.
Runtime error The code parses, but an operation fails while it executes. Inspect the failing expression and the values it uses: a value may be missing, have an unexpected type, or not yet be available.
Logic error The code runs, but the result or behavior is wrong. There may be no exception. Trace inputs, intermediate values, branches, and expected versus actual output. Add assertions or focused logs around the incorrect result.

MDN describes these categories and common debugging approaches in its JavaScript debugging and error handling guide and troubleshooting tutorial. An error’s type and message can narrow the search, but check the actual runtime state.

3. Inspect the statement and its values

Read the failing expression from the values outward. Ask what each variable should contain, where it was assigned, and whether the relevant operation has completed. Logging is a quick way to compare expectations with reality:

function renderUser(user) {
  console.log("user before render:", user);
  console.log("user type:", typeof user);

  if (!user) {
    console.error("renderUser received no user");
    return;
  }

  console.log("user name:", user.name);
  document.querySelector("#name").textContent = user.name ?? "Unknown";
}

Place logs immediately before the suspicious operation, and include enough context to tell which call produced them. Remove temporary logs when they are no longer useful; avoid logging secrets, tokens, or private user data.

For DOM errors, check whether the selector actually matched an element and whether the script ran after that element existed:

const button = document.querySelector("#save");

if (!button) {
  console.error("Expected #save to exist before attaching its handler");
} else {
  button.addEventListener("click", save);
}

If the element is created later, attach the handler after rendering it, or run setup after the document has been parsed. Do not hide a missing-element bug with optional chaining unless skipping the operation is genuinely the intended behavior.

4. Pause with a debugger when logs are not enough

Open the linked script in developer tools, click the line number to set a breakpoint, and reproduce the problem. When execution pauses, inspect local variables, the current scope, and the call stack. Step over or into the next operation to see where the value changes or the unexpected branch is taken. Browser interfaces vary, but the same basic tools are available in browser debuggers. MDN’s debugging guide describes breakpoints, call stacks, and scope inspection.

You can also pause when an exception is thrown by enabling the debugger’s pause-on-exceptions setting. This can reveal the original failure before a catch block handles it. Disable that setting afterward if expected exceptions make stepping through the app noisy.

5. Fix asynchronous failures separately

Async code often fails because a Promise is treated as though it already contains its eventual value, or because a rejection is not handled. For example, fetch() returns a Promise; it does not return parsed JSON directly. Await each step and handle both network errors and non-success HTTP responses:

async function loadProfile() {
  try {
    const response = await fetch("/api/profile");

    if (!response.ok) {
      throw new Error(`Profile request failed: HTTP ${response.status}`);
    }

    const profile = await response.json();
    console.log("profile:", profile);
    return profile;
  } catch (error) {
    console.error("Could not load profile:", error);
    throw error;
  }
}

loadProfile().catch((error) => {
  // Handle the failure at this boundary, such as by showing an error state.
  showProfileError(error);
});

Use await inside an async function, or attach a .catch() to a Promise chain. Handle the failure at the layer that can respond meaningfully; do not catch and discard it. A rejected Promise is distinct from a synchronous uncaught exception: the window error event reports synchronous script errors, while an unhandled Promise rejection uses unhandledrejection. A global handler that listens only for error will not report every async failure. See MDN on the window error event and unhandledrejection event.

For temporary diagnostics, these listeners can help distinguish the two classes. They do not replace handling errors at the operation that can recover or show a useful message:

window.addEventListener("error", (event) => {
  console.error("Uncaught script error:", event.message, event.filename, event.lineno);
});

window.addEventListener("unhandledrejection", (event) => {
  console.error("Unhandled Promise rejection:", event.reason);
});

6. Handle CORS errors at the right boundary

A CORS message means the browser enforced cross-origin access rules for a request. Inspect the failed request in the Network panel and the response headers. The server must allow the requesting origin; page JavaScript cannot grant itself permission to read a response that the server has not made available.

If you control the server, configure its CORS response for the intended origin, methods, and headers. If you do not control the remote server, use an API intended for browser access or a proxy that you control and are authorized to use. MDN explains the browser behavior and possible remedies in its CORS errors guide.

Setting mode: "no-cors" is not a way to read a blocked response. It produces an opaque response whose body and headers are unavailable to JavaScript. If code needs the response data, fix the server-side policy or use an appropriate server-side request path.

7. Map bundled errors back to source

If the console points to a minified or bundled file, check whether the build publishes a valid source map and whether developer tools have loaded it. Source maps let debugging tools show the original source corresponding to generated code. The generated response can identify a map through a SourceMap header; MDN documents the SourceMap header.

When the map is missing, inaccessible, stale, or does not match the deployed bundle, the displayed source location may be difficult to relate to your code. Check the build output and deployment together so the map corresponds to the exact JavaScript file served to the browser. Do not publish source maps if your deployment policy treats their contents as private.

8. Prevent repeat errors and confirm the fix

  • Use a JavaScript linter and formatter in development to catch some invalid or inconsistent code before it runs. A linter cannot identify every runtime or logic bug.
  • Validate external data at the boundary where it enters the app instead of assuming it always has the expected shape.
  • Represent loading, success, empty, and error states explicitly for asynchronous operations.
  • Keep a small reproduction case when an issue depends on a particular input or sequence of actions.
  • After changing the code, repeat the original steps and confirm both that the console error is gone and that the affected feature now behaves correctly.

For a page rendering problem, compare the rendered result before and after the fix at the same route and viewport. A screenshot can help record that visual state for a bug report or review; it does not replace reading the console and network errors.

Common JavaScript error messages and fixes

Console clue Likely cause What to check
SyntaxError or “unexpected token” The parser encountered invalid syntax, often near the reported location. Check delimiters, quotes, commas, keywords, and the preceding line. Confirm the code is being parsed as the intended script or module.
ReferenceError: x is not defined The identifier is unavailable in the current scope or was misspelled. Check spelling, declaration, module imports, and whether the value is used before its declaration or outside its scope.
TypeError involving a property or method The value is nullish or has a different shape or type than expected. Log the value before the operation. Check the data source and add a deliberate validation or fallback where appropriate.
“Cannot read properties of null” A DOM lookup returned null, or the value was not initialized. Check the selector, timing, and whether the element exists on this route or state.
Unhandled Promise rejection An async operation rejected without a handler. Inspect the rejection reason and add handling at the operation or call boundary that can respond.
CORS policy message The server response does not permit the page origin to read it. Inspect the Network request and server CORS headers. Change server configuration or use an authorized proxy path.
Location points to minified output The browser is showing generated code or cannot map it to original source. Check that a matching source map is published, accessible, and loaded by developer tools.
No error, but wrong output A logic error or incorrect assumption may produce a valid but unintended result. Compare expected and actual values through the relevant branches and transformations.

Performance, reliability, and cost

Browser developer tools, console logging, breakpoints, and linting are enough for the core diagnosis and do not require a paid debugging product. Keep high-volume logging out of production paths because it can add noise and expose sensitive values; use focused diagnostics and remove temporary instrumentation. A debugger pause changes timing, so an issue involving races or timing may behave differently while paused. Reproduce it without breakpoints after the fix as well.

For intermittent failures, record the non-sensitive context needed to reproduce them: route, action sequence, browser environment, request status, and relevant state transitions. Avoid assuming that one successful run proves the issue is fixed; repeat the original failing path, including the async or network conditions that mattered.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. It can capture a rendered page as an image or PDF, which is useful when you need a visual record of a page while investigating a rendering bug. A screenshot captures appearance; use the browser console and Network panel to diagnose JavaScript errors.

One GET request returns the capture. See the ScreenshotNeo API documentation for configuration and response details:

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners are accepted and removed before capture; newsletter popups and chat widgets are removed too. Each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed. Response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents using Claude, Cursor, or another MCP client.
  • The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan.

Sign up for 1,000 free screenshots a month with no card.

FAQ

Can I fix a JavaScript error by refreshing the page?

A refresh can clear a temporary state, but it does not fix a reproducible bug in the code or server response. Reproduce the failure and investigate its message, location, and inputs.

Why does the message look different in another browser?

Browsers can report the same underlying problem with different wording or interface labels. Use the error type, source location, stack, and runtime values as the shared clues.

Does a screenshot show the JavaScript error?

A screenshot shows rendered pixels, not the browser console or the cause of an exception. Use DevTools for diagnosis and a screenshot only as a visual record of the page state.

Will a global error handler catch every failure?

No. Synchronous script errors and unhandled Promise rejections use different reporting events, and locally handled failures may not reach either global event. Handle errors at the operation boundary that can recover or present an error state.