TrackJS https://trackjs.com Tue, 03 Mar 2026 19:22:50 +0000 Tue, 03 Mar 2026 19:22:50 +0000 TrackJS is a frontend native JavaScript error monitoring tool. Private by default, zero page impact, and built to give you the full story behind every production error. en-US 2013-2026 TrackJS LLC. All rights reserved. JavaScript Debugging JavaScript Error Monitoring https://trackjs.com/assets/images/brand/trackjs-icon-512x512.png https://trackjs.com/assets/images/brand/logo_charcoal_red.svg D03F40 https://trackjs.com/assets/images/brand/trackjs-icon-512x512.png TrackJS 512 512 How to Debug Code You Didn’t Write (your AI did) https://trackjs.com/blog/how-to-debug-code-you-didnt-write/ https://trackjs.com/blog/how-to-debug-code-you-didnt-write/ Thu, 26 Feb 2026 00:00:00 +0000 How to Debug Code You Didn’t Write (your AI did)

I was looking at a customer’s error report last week. A TypeError buried three callbacks deep in a checkout flow that made no sense. The code around it was clean, well-structured, and completely wrong about how the Stripe API actually works.

Turns out it was vibe-coded. Someone prompted their way through the integration, it passed code review because it looked reasonable, and it worked fine right up until a customer’s card got declined for the first time.

That’s the new normal. Maybe the code came from a coworker who left six months ago. Maybe a contractor. Maybe GitHub Copilot, Claude, or whatever autocomplete casino your team is using this week.

Doesn’t matter. The bug report still lands on your desk like you wrote the thing yourself.

And the instinct is to reverse-engineer the author’s assumptions and crimes against the event loop. But that approach is slow, expensive, and usually wrong.

Don’t Start With the Code

This is the first trap.

You open the file. You start reading. Fifteen minutes later you’re six functions deep, learning the personal philosophy of a stranger who thought nested ternaries were a personality trait.

Meanwhile, you still don’t know what actually broke.

Instead, start with the runtime failure:

  • What was the actual error?
  • Which browser did it happen in?
  • What was the user doing right before it happened?
  • Is this happening once, or to a lot of people?
  • Did it start after a deploy?

That gets you out of “read everything and hope” mode and into “find the failing path” mode.

Look for Assumptions, Not Just Mistakes

The bug is usually not “someone typed the wrong syntax.”

The bug is that the original author assumed something would always be true:

  • the API would always return data
  • the DOM would already be there
  • the state object would exist
  • the route would only change one way
  • the browser would behave like Chrome on their laptop
  • the user would click things in the “correct” order

That’s why unfamiliar code is so annoying. You’re not just finding a broken line. You’re finding the hidden agreement the code made with reality, and reality stopped cooperating.

Once you know the failed assumption, the fix usually gets a lot simpler.

Reconstruct the Scene

Most JavaScript bugs are not mysterious. They’re just missing context.

Cannot read properties of undefined is not helpful by itself. Neither is “Script error.” Neither is a screenshot from support that says “it stopped working.”

What you actually need is the story around the failure.

Did the user click checkout twice? Did a third-party script mutate the page? Did Safari decide today was the day it would be special?

Telemetry matters. Console logs matter. Network activity matters. User actions matter. Without that trail, you’re not debugging. You’re writing fan fiction about someone else’s code.

Get the Evidence From Production

Yes, this is the part where I talk about TrackJS. I know. Bear with me.

When you’re debugging code you didn’t write, you need more than the raw error message. You need the stack trace, the browser, the user actions, the console logs, and the network trail. You need enough context to understand what the code was trying to do before reality intervened.

That’s what TrackJS gives you. Not omniscience — just enough evidence that you can stop guessing and start fixing.

And if the error report still looks like browser nonsense, the AI Debugger can translate it back into normal human frustration. Which, honestly, is the most appropriate use of AI I’ve seen yet.

The Real Skill

Debugging code you didn’t write is not about becoming an archaeologist of every file in the repo. It’s about resisting the urge to read everything, starting from what actually failed, and working backward to the bad assumption.

That’s the skill. And it’s going to matter more every year, because your team is going to keep shipping code nobody fully understands. Some from coworkers. Some from contractors. A growing pile of it from AI that looked great in the diff and broke on the first edge case.

It all becomes your bug eventually.

The vibes won’t survive production. The evidence will.

]]>
Who Watches the Vibe Coder? https://trackjs.com/blog/who-watches-the-vibe-coder/ https://trackjs.com/blog/who-watches-the-vibe-coder/ Thu, 19 Feb 2026 00:00:00 +0000 Who Watches the Vibe Coder?

AI didn’t replace developers.

It replaced the part where you were forced to understand what you just shipped.

Now you can prompt your way to a feature, skim the diff, and merge something that “seems reasonable.” And then production does what production always does: finds the one weird browser + one slow network + one user flow that turns your “reasonable” code into a bonfire.

So who watches the vibe coder?

The Modern Threat Model: “It Looked Fine In The Chat Window”

Classic JavaScript failures still exist. Undefined references. Timing bugs. Bad data. Third-party scripts lighting themselves on fire.

The vibe-coded era adds a new class of risk: code that’s plausible, confident, and wrong in subtle ways.

Here’s what I see over and over:

  • Happy-path code with missing guards (because the AI assumed user is always defined, like a sweet summer child)
  • Copy-pasted patterns without the “why” (React hydration, async race conditions, event handler leaks… pick your poison)
  • Framework cargo culting (it used a hook! it must be correct!)
  • Invisible coupling (two “independent” changes that only break when they meet in production traffic)
  • Quiet failures (caught exceptions, swallowed promises, or “works on my machine” logic that never hits your local test data)

Code review helps, but it has a blind spot: reviewers also skim. Everyone skims. Especially when the diff is 600 lines. Looks good to me.

Why Code Review Doesn’t Save You

If you’re reviewing understanding, you’re good.

If you’re reviewing vibes, you’re gambling.

The hard bugs aren’t “syntax wrong.” They’re “timing wrong,” “data wrong,” “browser weird,” “user did something you didn’t anticipate,” or “third-party broke and took you with it.”

And those don’t show up until real users show up. When they do, debugging code you didn’t write is a whole different skill.

The Only Real Fix: Production Visibility

You don’t need more opinions about what might break.

You need a system that tells you what broke, who it hit, and what happened right before it broke.

In practice, that means:

  1. Errors captured from real browsers (not just your dev machine)
  2. Context that explains the story of the error
  3. A way to separate “random internet noise” from “this is our problem”

That’s the job.

Monitor Your Production Errors

This is exactly what TrackJS is for.

TrackJS watches your production JavaScript and gives you:

  • Actionable error reports (stack traces + the timeline of events that led to the error)
  • Filtering + ignore rules so you can ditch extension noise and third-party garbage
  • Impact visibility so you can see which errors are actually hurting users (and which ones are just embarrassing)

You can’t code review vibes. But you can watch what the vibes ship.

The Punchline

AI is going to keep writing code.

Non-engineers are going to keep shipping things they don’t fully understand.

And your site is going to keep being the place where all of that meets reality.

So: who watches the vibe coder?

You do. In production. With tooling that’s built for the messy, chaotic, real web.

If you want the easy button: start a TrackJS trial and let production tell you the truth before your users do.

]]>
How to fix `undefined is not an object (evaluating 'window.webkit.messageHandlers')` https://trackjs.com/javascript-errors/undefined-is-not-an-object-window-webkit-messagehandlers/ https://trackjs.com/javascript-errors/undefined-is-not-an-object-window-webkit-messagehandlers/ Tue, 03 Feb 2026 00:00:00 +0000 How to fix `undefined is not an object (evaluating 'window.webkit.messageHandlers')`

This error is Safari’s way of saying, “you’re trying to use an iOS app WebView bridge… but you’re not in an iOS app WebView.”

window.webkit.messageHandlers is the JavaScript API for talking from a page loaded inside WKWebView back to native code. The native app registers a handler name, and JavaScript can call window.webkit.messageHandlers.<name>.postMessage(...). Here’s how Apple documents the contract.

When Safari throws:

undefined is not an object (evaluating 'window.webkit.messageHandlers')

…it means your code ran in an environment where that bridge simply does not exist.

The Problem

Some code on your page is doing this:


// This only works inside a WKWebView where the native app
// has registered the handler name
window.webkit.messageHandlers.nativeBridge.postMessage({
  type: "hello",
  payload: { ok: true }
});

On a normal website in Safari (and always in Chrome, Firefox, or Edge), window.webkit is usually missing, or messageHandlers is missing, so this explodes.

What’s Actually Happening

This error almost always means you shipped WKWebView-specific code into a context where it does not belong.

The most common case is shared code between a normal website and an iOS app that embeds the site in a WKWebView. The bridge works fine inside the app, then regular Safari users hit the same code path and everything falls apart.

Another frequent source is third-party or hybrid tooling that assumes it’s running inside an in-app browser. Cordova is a classic example, it uses window.webkit.messageHandlers.cordova.postMessage(...) when running in WKWebView mode, and fails loudly if that bridge is missing. There are plenty of bug reports that boil down to exactly this mismatch, like this one on GitHub.

Even inside a WKWebView, this error can happen if your native app forgot to register the handler, registered it under a different name, or registered it too late. JavaScript only gets messageHandlers.<name> if native code registered that exact name via WKUserContentController. Apple documents that setup here.

How to Fix It

Guard the call with feature detection

If your code runs on both the open web and inside an app, this is non-negotiable.


function postToNative(handlerName, message) {
  const bridge =
    window.webkit &&
    window.webkit.messageHandlers &&
    window.webkit.messageHandlers[handlerName] &&
    typeof window.webkit.messageHandlers[handlerName].postMessage === "function";

  if (!bridge) {
    // Not in WKWebView, or handler not registered.
    // Decide what "web fallback" means for your app.
    return false;
  }

  window.webkit.messageHandlers[handlerName].postMessage(message);
  return true;
}

// Usage
postToNative("nativeBridge", { type: "hello" });

If you’re using modern JavaScript, optional chaining makes this a lot cleaner:


if (window.webkit?.messageHandlers?.nativeBridge?.postMessage) {
  window.webkit.messageHandlers.nativeBridge.postMessage({ type: "hello" });
}

If you own the iOS app, verify the native side

Your JavaScript can be perfect and this will still fail if the app didn’t register the handler correctly.

The handler name must match exactly, and it must be registered before the page runs code that calls postMessage. Here’s Apple’s reference for that registration step.

Monitor your production errors

This one is famously environment-specific. Some users are inside an app, most are not. You want to know how often this is happening, which pages trigger it, and whether it correlates with real user pain.

That’s why you monitor production errors instead of guessing. Using something like TrackJS makes it obvious whether this is a broken integration you need to fix, or harmless noise from a context you don’t support.

When You Can Ignore It

You can usually ignore this error if it’s clearly coming from third-party code you don’t control, it only affects optional in-app features, and it isn’t impacting real users. If it’s tied to critical flows like auth, payments, or navigation inside your app, it’s not ignorable. That’s a real bug.

Summary

window.webkit.messageHandlers only exists inside WKWebView when native code registers a handler. If you try to use it on the open web, Safari throws:

undefined is not an object (evaluating 'window.webkit.messageHandlers')

Fix it with feature detection, and if you control the native app, make sure the handler is registered correctly and early. Then watch it in production so you know whether it actually matters.

]]>
How to fix `Unexpected end of input` https://trackjs.com/javascript-errors/unexpected-end-of-input/ https://trackjs.com/javascript-errors/unexpected-end-of-input/ Wed, 28 Jan 2026 00:00:00 +0000 How to fix `Unexpected end of input`

SyntaxError: Unexpected end of input means JavaScript reached the end of your code or data but was still expecting more. The parser opened something (a function, an object, a string) and never found the closing character it needed.

This error appears in two common scenarios: incomplete JavaScript code with missing brackets or quotes, and JSON.parse() receiving empty or truncated data. The first is typically a development-time bug, while the second often surfaces in production when API responses fail.

The Problem

The JavaScript parser reads your code character by character, tracking opening and closing delimiters. When it reaches the end of the input while still waiting for a closing }, ], ), or quote, it throws this error.

Code syntax variant:

function calculateTotal(items) {
  let total = 0;
  items.forEach(item => {
    total += item.price;
  // Missing closing brace for forEach
// Missing closing brace for function

// SyntaxError: Unexpected end of input

JSON parsing variant:

// Server returned empty response
const response = await fetch('/api/data');
const data = await response.json();
// SyntaxError: Unexpected end of JSON input

The JSON variant shows as Unexpected end of JSON input and occurs when JSON.parse() (or response.json()) receives an empty string, truncated data, or non-JSON content.

Understanding the Root Cause

“Unexpected end of input” stems from different scenarios depending on whether you’re dealing with code syntax or JSON parsing:

1. Missing Closing Brackets or Braces

Most common during development: Forgetting to close a function, object, array, or block statement leaves the parser expecting more input.

How to identify: The error points to the end of your file or code block. Check for mismatched { }, [ ], or ( ) pairs.

2. Unclosed Strings or Template Literals

Starting a string with a quote or backtick and not providing the closing delimiter creates an unterminated string.

How to identify: Look for strings that span multiple lines unexpectedly or missing closing quotes.

3. Empty API Responses

Servers sometimes return empty responses (status 204, empty body with 200, or error conditions) when your code expects JSON data.

How to identify: Check the Network tab in DevTools. Look for responses with empty bodies or non-JSON content types.

4. Server Returning HTML Instead of JSON

When servers encounter errors, they often return HTML error pages instead of JSON. Your code tries to parse <!DOCTYPE html> as JSON and fails.

How to identify: Inspect the response body in DevTools. If you see HTML when expecting JSON, the server returned an error page.

5. Truncated Network Responses

Network interruptions, CDN issues, or proxy problems can cut off responses mid-transmission, leaving incomplete JSON.

How to identify: Responses that look like valid JSON but end abruptly, often in the middle of a string or object.

6. Parsing Already-Parsed Data

Calling JSON.parse() on data that’s already a JavaScript object (not a JSON string) throws this error.

How to identify: Log the value and its type before parsing. If it’s already an object, you don’t need to parse it.

How to Fix “Unexpected end of input”

Troubleshooting Checklist

  • Check DevTools console for the line number where parsing stopped
  • Verify all opening brackets have matching closing brackets
  • Inspect API responses in the Network tab before parsing
  • Confirm the server returns application/json content type
  • Test what happens when the server returns an error or empty response
  • Wrap JSON parsing in try/catch blocks

Step 1: Fix Code Syntax Issues

For development-time syntax errors, use your editor’s bracket matching:

// Use an editor with bracket matching to find mismatches
// VSCode: Cmd+Shift+P -> "Go to Bracket"
// Most editors highlight matching brackets when cursor is on one

// Before: Missing closures
function processData(items) {
  return items.map(item => {
    return {
      id: item.id,
      name: item.name
    // missing } for object
  // missing } for arrow function
// missing } for function

// After: All brackets matched
function processData(items) {
  return items.map(item => {
    return {
      id: item.id,
      name: item.name
    };
  });
}

Run your code through a linter like ESLint to catch these errors before they reach production.

Step 2: Validate Responses Before Parsing

Check that you have valid JSON before attempting to parse:

async function fetchData(url) {
  const response = await fetch(url);

  // Check if response is OK
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  // Check content type
  const contentType = response.headers.get('content-type');
  if (!contentType?.includes('application/json')) {
    const text = await response.text();
    throw new Error(`Expected JSON, got ${contentType}: ${text.slice(0, 100)}`);
  }

  // Now safe to parse
  return response.json();
}

Step 3: Handle Empty Responses

Some endpoints legitimately return empty responses. Handle this gracefully:

async function fetchWithEmptyHandling(url) {
  const response = await fetch(url);

  // Handle 204 No Content
  if (response.status === 204) {
    return null;
  }

  // Check for empty body
  const text = await response.text();
  if (!text || text.trim() === '') {
    return null;
  }

  // Parse the JSON
  try {
    return JSON.parse(text);
  } catch (error) {
    console.error('Failed to parse response:', text.slice(0, 200));
    throw error;
  }
}

Step 4: Wrap JSON.parse in Try/Catch

Always protect JSON parsing with error handling:

function safeJsonParse(jsonString, fallback = null) {
  // Handle empty or falsy input
  if (!jsonString || typeof jsonString !== 'string') {
    return fallback;
  }

  try {
    return JSON.parse(jsonString);
  } catch (error) {
    console.error('JSON parse failed:', error.message);
    console.error('Input was:', jsonString.slice(0, 100));
    return fallback;
  }
}

// Usage
const data = safeJsonParse(localStorage.getItem('userData'), {});

Step 5: Debug Server Responses

When the error occurs in production, you need visibility into what the server actually returned:

async function debuggableFetch(url, options = {}) {
  try {
    const response = await fetch(url, options);
    const text = await response.text();

    // Log response details for debugging
    console.log('Response status:', response.status);
    console.log('Content-Type:', response.headers.get('content-type'));
    console.log('Body length:', text.length);

    if (!text) {
      console.warn('Empty response body from:', url);
      return null;
    }

    return JSON.parse(text);
  } catch (error) {
    console.error('Fetch/parse error for', url, error);
    throw error;
  }
}

Step 6: Monitor JSON Parsing Errors in Production

This isn’t an error you’ll catch in development if it’s caused by intermittent server issues or network problems. Production error monitoring captures these failures with the context you need: what URL was requested, what the response looked like, and what the user was doing when it happened.

Track patterns like specific endpoints that return empty responses, times of day when truncation occurs, or user segments affected by server errors.

When to Investigate This Error

“Unexpected end of input” errors deserve different responses depending on the cause:

Investigate immediately:

  • JSON parsing errors in production affecting user functionality
  • Errors that correlate with specific API endpoints
  • Sudden spikes after deployments or infrastructure changes
  • Errors affecting checkout, authentication, or data submission

Development-time fixes:

  • Syntax errors from missing brackets (fix before committing)
  • Template literal or string issues (your linter should catch these)

May be acceptable:

  • Occasional parsing failures from unstable third-party APIs
  • Errors from API responses you can gracefully handle with fallbacks

Summary

Unexpected end of input means the JavaScript parser expected more code or data than it received. For syntax errors, check for missing closing brackets, braces, and quotes. For JSON errors, validate responses before parsing and handle empty or malformed data gracefully.

The JSON parsing variant is particularly sneaky because it only appears when servers misbehave, which often means production-only bugs. Build defensive parsing that expects things to go wrong, and monitor for patterns that indicate server-side issues.

]]>
How to fix `undefined has no properties` https://trackjs.com/javascript-errors/undefined-has-no-properies/ https://trackjs.com/javascript-errors/undefined-has-no-properies/ Thu, 22 Jan 2026 00:00:00 +0000 How to fix `undefined has no properties`

This is Firefox’s way of telling you that you tried to destructure something that doesn’t exist. The error occurs when JavaScript attempts to extract properties from null or undefined using destructuring syntax. Since neither value has properties, the operation fails.

Firefox’s error message is less specific than other browsers, making it slightly harder to identify as a destructuring problem. You might also see null has no properties when destructuring a null value specifically.

Other browsers show the same error with different messages:

The Problem

“undefined has no properties” is a TypeError that occurs when you try to use destructuring assignment on null or undefined. JavaScript can only destructure objects and arrays, so attempting to extract properties from values that have no properties throws this error.

Here’s the classic scenario:

// API returns null when user not found
const userData = null;

// Firefox throws: TypeError: userData is null
const { name, email } = userData;

The error also occurs with undefined:

// Function called without arguments
function processUser({ name, email }) {
  console.log(name, email);
}

// Firefox throws: TypeError: (intermediate value) is undefined
processUser(); // No argument passed

Key point: This error always means your code expected an object but received null or undefined instead. The fix is ensuring you have a valid object before destructuring.

Understanding the Root Cause

“undefined has no properties” stems from several common patterns in modern JavaScript applications:

1. API Responses Returning Null

Most common cause: Backend APIs return null when requested data doesn’t exist, but your frontend code assumes it will always receive an object.

How to identify: Errors correlate with API calls, especially for optional or user-specific data like profiles, settings, or search results.

2. Missing Function Arguments

Functions that destructure their parameters fail when called without arguments, since missing parameters default to undefined.

How to identify: Stack traces point to function definitions with destructuring patterns in the parameter list.

3. Async Data Loading Race Conditions

React, Vue, and Angular components often try to destructure state or props before async data finishes loading.

How to identify: Errors occur during component mounting or immediately after page load, often disappearing after data loads.

4. Array Destructuring Beyond Bounds

Destructuring arrays that are shorter than expected, or destructuring when the array itself is undefined.

How to identify: Errors involving array methods like find(), filter(), or API responses returning arrays.

5. Optional Object Properties

Nested destructuring fails when intermediate properties are null or undefined.

How to identify: Deep destructuring patterns like const { user: { profile: { name } } } where any level might be missing.

How to Fix “undefined has no properties”

Quick Troubleshooting Checklist

  • Add fallback empty objects using ?? or || operators
  • Set default parameter values for function arguments
  • Verify API response structure before destructuring
  • Add loading state checks in React/Vue/Angular components
  • Use optional chaining when destructuring isn’t necessary

If basic checks don’t resolve the issue, follow these systematic fixes:

Step 1: Add Fallback Objects with Nullish Coalescing

The most reliable fix is providing an empty object fallback:

// Before: Crashes if userData is null or undefined
const { name, email } = userData;

// After: Safe destructuring with fallback
const { name, email } = userData ?? {};

// You can also use logical OR (works for all falsy values)
const { name, email } = userData || {};

The ?? operator only applies the fallback for null or undefined, while || applies it for any falsy value. For destructuring, ?? is usually the better choice.

Step 2: Set Default Function Parameters

Always provide defaults for function parameters that will be destructured:

// Before: Fails when called without arguments
function createUser({ name, email, role }) {
  return { name, email, role };
}

// After: Safe with default empty object
function createUser({ name, email, role } = {}) {
  return { name, email, role };
}

// Now these all work without errors
createUser();
createUser({ name: 'Alice' });
createUser({ name: 'Bob', role: 'admin' });

Step 3: Handle Nested Destructuring Safely

Nested destructuring is particularly fragile. Protect each level:

// Dangerous: Multiple points of failure
const { user: { profile: { name } } } = apiResponse;

// Safe approach: Fallbacks at each level
const { user } = apiResponse ?? {};
const { profile } = user ?? {};
const { name } = profile ?? {};

// Alternative: Use optional chaining instead of destructuring
const name = apiResponse?.user?.profile?.name;

Step 4: Handle React Component State Properly

In React, always account for loading states:

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser(userId)
      .then(data => setUser(data))
      .finally(() => setLoading(false));
  }, [userId]);

  // Guard against destructuring null/undefined
  if (loading) return <div>Loading...</div>;
  if (!user) return <div>User not found</div>;

  // Safe to destructure after validation
  const { name, email, avatar } = user;

  return (
    <div>
      <img src={avatar} alt={name} />
      <h1>{name}</h1>
      <p>{email}</p>
    </div>
  );
}

Step 5: Validate API Responses

Add explicit checks for API data before destructuring:

async function fetchUserData(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();

  // Validate response structure before destructuring
  if (!data || typeof data !== 'object') {
    console.warn('Invalid user data received:', data);
    return null;
  }

  const { name, email, preferences } = data;

  return {
    name: name ?? 'Unknown User',
    email: email ?? '',
    preferences: preferences ?? {}
  };
}

Step 6: Use TypeScript for Prevention

TypeScript catches destructuring errors at compile time:

// TypeScript interface defines expected shape
interface User {
  id: string;
  name: string;
  email: string;
  profile?: {
    avatar?: string;
  };
}

// Function requires User type
function displayUser(user: User) {
  const { name, email } = user; // Safe: user is guaranteed
  const { avatar = '/default.png' } = user.profile || {};
  return `${name} (${email})`;
}

// With optional parameter, TypeScript enforces checks
function processUser(user?: User) {
  if (!user) {
    return 'No user provided';
  }
  const { name } = user; // TypeScript knows user exists here
  return name;
}

Step 7: Monitor Destructuring Errors in Production

Track these errors to understand how often they occur and whether they impact users. Error monitoring services like TrackJS capture the full context around destructuring failures, showing you which API responses or user flows trigger the problem.

Production monitoring reveals patterns you won’t see in development, like errors that only occur with specific data combinations or under race conditions.

When to Ignore This Error

This error should rarely be ignored since it indicates real problems with data flow. However, consider the context:

  • Third-party library bugs: Some libraries may throw this error internally with their own error handling
  • Development hot reload: Temporary errors during fast refresh cycles
  • Browser extension interference: Extensions modifying page data

Investigate further when you see:

  • User-facing impact: Broken pages or missing content
  • Consistent patterns: Same destructuring operation failing repeatedly
  • Core functionality: Errors affecting checkout, authentication, or data submission
  • Sudden increases: Spikes after API changes or deployments

Summary

“undefined has no properties” is Firefox’s error message when you try to destructure null or undefined. Chrome shows “Cannot destructure property” and Safari shows “Right side of assignment cannot be destructured” for the same underlying issue.

The fix is always about ensuring you have a valid object before destructuring:

  1. Add fallback objects with ?? or || operators
  2. Set default parameters on functions that destructure arguments
  3. Validate API responses before destructuring
  4. Use loading states in components to prevent premature destructuring

Remember: Destructuring is convenient syntax, but it assumes your data exists. When working with data that might be missing, either add explicit fallbacks or use optional chaining instead.

]]>
How to fix `Cannot destructure property 'x' of 'y' as it is undefined` https://trackjs.com/javascript-errors/cannot-destructure-property-x-of-y/ https://trackjs.com/javascript-errors/cannot-destructure-property-x-of-y/ Tue, 13 Jan 2026 00:00:00 +0000 How to fix `Cannot destructure property 'x' of 'y' as it is undefined`

This Chrome error appears when your code tries to destructure properties from a value that is undefined or null. Destructuring has become the default way to extract values in modern JavaScript, making this one of the most frequent errors in React, Vue, and Node.js applications.

The error message helpfully tells you which property failed and what the source value was, but the fix requires understanding why your data wasn’t there in the first place.

Other browsers show the same error with different messages:

Destructuring errors are closely related to property access errors:

The Problem

“Cannot destructure property ‘x’ of ‘y’ as it is undefined” is a TypeError that occurs when JavaScript attempts to extract properties from a value that doesn’t exist. The destructuring syntax assumes an object or array is present, and fails when it encounters undefined or null.

The error commonly appears in scenarios like this:


// API response that hasn't loaded yet
const [user, setUser] = useState();

function UserProfile() {
  // Error: Cannot destructure property 'name' of 'user' as it is undefined
  const { name, email } = user;

  return <div>{name}</div>;
}

Key point: Unlike dot notation (user.name) which returns undefined silently, destructuring throws an error immediately when the source is missing.

Understanding the Root Cause

“Cannot destructure property” errors stem from assumptions about data availability that don’t hold true at runtime:

1. Async Data Not Yet Loaded

Most common in React and Vue: Components render before API data arrives, causing destructuring to fail on the initial render cycle.

How to identify: Errors occur during component mounting, often disappearing after data loads.

2. Function Parameters Without Defaults

Functions that expect objects as parameters fail when called without arguments or with undefined.

How to identify: Errors trace back to function calls, particularly event handlers or callbacks.

3. API Response Structure Changes

Backend changes can remove expected properties or return different data shapes, breaking frontend destructuring.

How to identify: Errors appear after API deployments or when handling edge cases like empty results.

4. Optional Chaining Gaps

Code that uses optional chaining (?.) for some operations but forgets to protect destructuring.

How to identify: Mixed patterns in the codebase where some data access is protected and some isn’t.

5. Array Destructuring Position Errors

Destructuring arrays that are shorter than expected, or destructuring when the array itself is undefined.

How to identify: Errors involving array methods like find(), filter(), or API responses returning arrays.

How to Fix “Cannot destructure property”

Quick Troubleshooting Checklist

  • Add default values to destructuring assignments
  • Check if data is loaded before destructuring
  • Add fallback empty objects for function parameters
  • Use conditional rendering in React/Vue components
  • Validate API responses before accessing properties
  • Consider TypeScript for compile-time safety

If basic checks don’t resolve the issue, follow these systematic fixes:

Step 1: Add Default Values to Destructuring

Provide fallback values directly in the destructuring pattern:


// Before: Fails if user is undefined
const { name, email } = user;

// After: Falls back to empty object, then to default values
const { name = 'Guest', email = '' } = user || {};

// For nested destructuring
const { profile: { avatar = '/default.png' } = {} } = user || {};

// Array destructuring with defaults
const [first = 'N/A', second = 'N/A'] = items || [];

Step 2: Protect Function Parameters

Add default empty objects to function signatures:


// Before: Fails if called without arguments
function processUser({ name, email }) {
  console.log(name, email);
}

// After: Safe with default parameter
function processUser({ name, email } = {}) {
  console.log(name || 'Unknown', email || 'No email');
}

// Arrow function equivalent
const processUser = ({ name, email } = {}) => {
  console.log(name, email);
};

// With nested defaults
function updateSettings({ theme = 'light', notifications = {} } = {}) {
  const { email = true, push = false } = notifications;
  // Safe to use all values
}

Step 3: Handle Loading States Properly

Prevent destructuring until data is available:


function UserProfile() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser()
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(err => {
        console.error('Failed to load user:', err);
        setLoading(false);
      });
  }, []);

  // Guard against destructuring undefined
  if (loading) return <div>Loading...</div>;
  if (!user) return <div>User not found</div>;

  // Safe to destructure after guards
  const { name, email, profile } = user;

  return (
    <div>
      <h1>{name}</h1>
      <p>{email}</p>
    </div>
  );
}

Step 4: Validate API Responses

Check response structure before destructuring:


async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    const data = await response.json();

    // Validate before destructuring
    if (!data || typeof data !== 'object') {
      console.warn('Invalid user data format:', data);
      return { name: 'Unknown', email: '', id: null };
    }

    // Safe destructuring with defaults
    const {
      id = null,
      name = 'Unknown',
      email = '',
      preferences = {}
    } = data;

    return { id, name, email, preferences };

  } catch (error) {
    console.error('Failed to fetch user:', error);
    return { name: 'Unknown', email: '', id: null };
  }
}

Step 5: Use TypeScript for Prevention

TypeScript catches destructuring errors at compile time:


// TypeScript interface defines expected shape
interface User {
  id: string;
  name: string;
  email: string;
  profile?: {
    avatar?: string;
    bio?: string;
  };
}

// Function requires User type, compiler warns if undefined possible
function displayUser(user: User) {
  const { name, email } = user; // Safe: user is guaranteed to exist
  const { avatar = '/default.png' } = user.profile || {}; // Handle optional
  return `${name} (${email})`;
}

// With optional parameter, TypeScript enforces checks
function processUser(user?: User) {
  if (!user) {
    return 'No user provided';
  }
  const { name } = user; // TypeScript knows user exists here
  return name;
}

Step 6: Monitor Destructuring Errors in Production

Track these errors to understand how often they occur and whether they impact users. Error monitoring services like TrackJS capture the full context around destructuring failures, showing you which API responses or user flows trigger the problem.

Production monitoring reveals patterns you won’t see in development, like errors that only occur with specific data combinations or under race conditions.

When to Ignore This Error

“Cannot destructure property” errors should rarely be ignored since they typically indicate broken functionality. However, consider the context:

  • Third-party libraries: External code with its own data handling issues
  • Development hot reload: Temporary errors during code changes
  • Browser extensions: Extensions modifying page data unexpectedly

Investigate further when you see:

  • Consistent patterns: Regular errors affecting user workflows
  • Critical features: Errors in checkout, forms, or authentication
  • Post-deployment spikes: New errors after code changes
  • User complaints: Reports of missing data or broken displays

Summary

“Cannot destructure property ‘x’ of ‘y’ as it is undefined” happens when destructuring tries to extract properties from missing data. Unlike regular property access, destructuring fails loudly, which is actually helpful for catching data flow problems early.

The fix involves adding default values, guarding against missing data before destructuring, and properly handling async loading states. TypeScript provides compile-time protection that eliminates most of these runtime errors entirely.

Remember: Destructuring assumes your data exists. In modern applications where data comes from APIs, user input, and async operations, that assumption needs explicit validation.

]]>
How to fix `Right side of assignment cannot be destructured` https://trackjs.com/javascript-errors/right-side-of-assignment-cannot-be-destructured/ https://trackjs.com/javascript-errors/right-side-of-assignment-cannot-be-destructured/ Wed, 07 Jan 2026 00:00:00 +0000 How to fix `Right side of assignment cannot be destructured`

This is Safari’s way of telling you that you tried to destructure something that doesn’t exist. The error occurs when JavaScript attempts to extract properties from null or undefined using destructuring syntax. Since neither value has properties, the operation fails.

This error frequently appears in applications dealing with API responses, database queries, or any async data that might not be available when your code expects it.

Other browsers show the same error with different messages:

The Problem

“Right side of assignment cannot be destructured” is a TypeError that occurs when you try to use destructuring assignment on null or undefined. JavaScript can only destructure objects and arrays, so attempting to extract properties from values that have no properties throws this error.

Here’s the classic scenario:

// API returns null when user not found
const userData = null;

// Safari throws: Right side of assignment cannot be destructured
const { name, email } = userData;

The error also occurs with undefined:

// Function called without arguments
function processUser({ name, email }) {
  console.log(name, email);
}

// Safari throws: Right side of assignment cannot be destructured
processUser(); // No argument passed, so it's undefined

Key point: This error always means your code expected an object but received null or undefined instead. The fix is ensuring you have a valid object before destructuring.

Understanding the Root Cause

“Right side of assignment cannot be destructured” stems from several common patterns in modern JavaScript applications:

1. API Responses Returning Null

Most common cause: Backend APIs return null when requested data doesn’t exist, but your frontend code assumes it will always receive an object.

How to identify: Errors correlate with API calls, especially for optional or user-specific data like profiles, settings, or search results.

2. Missing Function Arguments

Functions that destructure their parameters fail when called without arguments, since missing parameters default to undefined.

How to identify: Stack traces point to function definitions with destructuring patterns in the parameter list.

3. Async Data Loading Race Conditions

React, Vue, and Angular components often try to destructure state or props before async data finishes loading.

How to identify: Errors occur during component mounting or immediately after page load, often disappearing after data loads.

4. DOM Query Results

Methods like document.querySelector() return null when no matching element exists. Destructuring these results fails.

How to identify: Errors occur when accessing element properties or dataset values from DOM queries.

5. Array Access Beyond Bounds

Accessing array indices that don’t exist returns undefined, which can’t be destructured.

How to identify: Errors happen when destructuring items from arrays that may be empty or shorter than expected.

6. Optional Object Properties

Nested destructuring fails when intermediate properties are null or undefined.

How to identify: Deep destructuring patterns like const { user: { profile: { name } } } where any level might be missing.

How to Fix “Right side of assignment cannot be destructured”

Quick Troubleshooting Checklist

  • Add fallback empty objects using ?? or || operators
  • Set default parameter values for function arguments
  • Verify API response structure before destructuring
  • Use optional chaining for property access when destructuring isn’t necessary
  • Add loading state checks in React/Vue/Angular components
  • Validate DOM query results before accessing properties

If basic checks don’t resolve the issue, follow these systematic fixes:

Step 1: Add Fallback Objects with Nullish Coalescing

The most reliable fix is providing an empty object fallback:

// Before: Crashes if userData is null or undefined
const { name, email } = userData;

// After: Safe destructuring with fallback
const { name, email } = userData ?? {};

// You can also use logical OR (works for all falsy values)
const { name, email } = userData || {};

The ?? operator only applies the fallback for null or undefined, while || applies it for any falsy value. For destructuring, ?? is usually the better choice.

Step 2: Set Default Function Parameters

Always provide defaults for function parameters that will be destructured:

// Before: Fails when called without arguments
function createUser({ name, email, role }) {
  return { name, email, role };
}

// After: Safe with default empty object
function createUser({ name, email, role } = {}) {
  return { name, email, role };
}

// Now these all work without errors
createUser();                          // Returns { name: undefined, email: undefined, role: undefined }
createUser({ name: 'Alice' });         // Returns { name: 'Alice', email: undefined, role: undefined }
createUser({ name: 'Bob', role: 'admin' });

Step 3: Handle Nested Destructuring Safely

Nested destructuring is particularly fragile. Protect each level:

// Dangerous: Multiple points of failure
const { user: { profile: { name } } } = apiResponse;

// Safe approach: Fallbacks at each level
const { user } = apiResponse ?? {};
const { profile } = user ?? {};
const { name } = profile ?? {};

// Alternative: Use optional chaining instead of destructuring
const name = apiResponse?.user?.profile?.name;

Step 4: Validate API Responses

Add explicit checks for API data before destructuring:

async function fetchUserData(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();

  // Validate response structure before destructuring
  if (!data || typeof data !== 'object') {
    console.warn('Invalid user data received:', data);
    return null;
  }

  const { name, email, preferences } = data;

  return {
    name: name ?? 'Unknown User',
    email: email ?? '',
    preferences: preferences ?? {}
  };
}

Step 5: Handle React Component State Properly

In React, always account for loading states:

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser(userId)
      .then(data => setUser(data))
      .finally(() => setLoading(false));
  }, [userId]);

  // Guard against destructuring null/undefined
  if (loading) return <div>Loading...</div>;
  if (!user) return <div>User not found</div>;

  // Safe to destructure after validation
  const { name, email, avatar } = user;

  return (
    <div>
      <img src={avatar} alt={name} />
      <h1>{name}</h1>
      <p>{email}</p>
    </div>
  );
}

Step 6: Protect DOM Query Results

Always check DOM queries before destructuring their results:

// Dangerous: querySelector returns null if element doesn't exist
const { dataset: { userId } } = document.querySelector('.user-card');

// Safe: Check element exists first
const userCard = document.querySelector('.user-card');
const userId = userCard?.dataset?.userId;

// Or with explicit validation
const userCard = document.querySelector('.user-card');
if (userCard) {
  const { dataset: { userId, userRole } } = userCard;
  initializeUser(userId, userRole);
}

Step 7: Monitor for Production Errors

Destructuring errors often only appear in production when real users encounter edge cases your tests didn’t cover. Error monitoring services like TrackJS capture these errors with full context about which destructuring operation failed and what the actual value was.

This visibility helps you identify patterns like specific API endpoints returning unexpected data or certain user flows that hit edge cases more frequently.

When to Ignore This Error

This error should rarely be ignored since it indicates real problems with data flow. However, consider the context:

  • Third-party library bugs: Some libraries may throw this error internally with their own error handling
  • Development hot reload: Temporary errors during fast refresh cycles
  • Browser extension interference: Extensions modifying page data

Investigate further when you see:

  • User-facing impact: Broken pages or missing content
  • Consistent patterns: Same destructuring operation failing repeatedly
  • Core functionality: Errors affecting checkout, authentication, or data submission
  • Sudden increases: Spikes after API changes or deployments

Summary

“Right side of assignment cannot be destructured” is Safari’s error message when you try to destructure null or undefined. Chrome shows “Cannot destructure property” and Firefox shows “undefined has no properties” for the same underlying issue.

The fix is always about ensuring you have a valid object before destructuring:

  1. Add fallback objects with ?? or || operators
  2. Set default parameters on functions that destructure arguments
  3. Validate API responses before destructuring
  4. Use loading states in components to prevent premature destructuring
  5. Check DOM queries return elements before accessing their properties

Remember: Destructuring is convenient syntax, but it assumes your data exists. When working with data that might be missing, either add explicit fallbacks or use optional chaining instead.

]]>
How to fix 404 Errors from WebPageTest.org Bot https://trackjs.com/javascript-errors/404-webpagetest-bot/ https://trackjs.com/javascript-errors/404-webpagetest-bot/ Tue, 23 Dec 2025 00:00:00 +0000 How to fix 404 Errors from WebPageTest.org Bot

If you’ve noticed a spike of 404 errors for files like ads.txt, security.txt, sellers.json, or various /.well-known/ paths, the culprit is likely the WebPageTest.org bot. Unlike simple crawlers, WebPageTest uses real Chromium browsers to test websites, which means JavaScript executes and your error monitoring captures the failed network requests.

These errors don’t indicate problems with your website. They’re the result of WebPageTest probing for standard configuration files that most websites don’t have.

Important: These errors come from an external testing service, not from your users or your application code.

The Problem

WebPageTest.org is a popular web performance testing tool that uses real browsers to analyze websites. When WebPageTest (or services built on it, like HTTP Archive) scans your site, it checks for various well-known configuration files. If those files don’t exist, the requests return 404 errors.

Because WebPageTest runs in a real Chromium browser, your JavaScript error monitoring picks up these failed network requests just like it would for any other browser. You’ll see 404 errors like:

  • GET /dsrdelete.json
  • GET /ads.txt
  • GET /sellers.json
  • GET /.well-known/security.txt
  • GET /.well-known/apple-app-site-association

Key point: These are bot-generated requests checking for files your site may legitimately not have. The 404 response is correct behavior.

Understanding the Root Cause

WebPageTest bot errors appear in your JavaScript monitoring for a specific reason:

Real Browser Execution

Most web crawlers make simple HTTP requests without executing JavaScript. WebPageTest is different. It launches actual Chromium browser instances to render pages, run JavaScript, and measure real-world performance. This means:

  • Your analytics and error monitoring scripts execute
  • Network requests (including failed ones) are visible to client-side tools
  • TrackJS captures these 404 errors because they happen in a real browser context

What Files Does WebPageTest Check?

WebPageTest probes for numerous standard configuration files:

Advertising Files:

  • /ads.txt - Authorized Digital Sellers
  • /app-ads.txt - Authorized Sellers for Apps
  • /sellers.json - Supply chain transparency
  • /dsrdelete.json - IAB Data Deletion Request Framework

Security Files:

  • /.well-known/security.txt - Security contact information
  • /.well-known/change-password - Password change URL

Authentication and Identity:

  • /.well-known/passkey-endpoints - Passkey configuration
  • /.well-known/webauthn - WebAuthn configuration
  • /.well-known/web-identity - Federated identity

Mobile App Integration:

  • /.well-known/apple-app-site-association - iOS Universal Links
  • /.well-known/assetlinks.json - Android App Links

Privacy and Platform:

  • /.well-known/gpc.json - Global Privacy Control
  • /.well-known/privacy-sandbox-attestations.json - Chrome Privacy Sandbox
  • /.well-known/related-website-set.json - Related Website Sets

Other:

  • /llms.txt - LLM crawler instructions
  • /.well-known/resource-that-should-not-exist-whose-status-code-should-not-be-200/ - HTTP compliance test

If you’re seeing 404 errors for several of these files around the same time, WebPageTest is almost certainly the source.

Who Uses WebPageTest?

Beyond individual developers testing their sites, WebPageTest infrastructure powers larger projects:

  • HTTP Archive runs monthly crawls of millions of websites using a private WebPageTest instance
  • Research projects use WebPageTest for web standards adoption studies
  • Enterprise monitoring services build on WebPageTest’s capabilities

This means your site might be scanned by WebPageTest even if you’ve never used it directly.

How to Fix WebPageTest Bot Errors

Since these errors come from external bots rather than your users, the solution is filtering them from your monitoring.

Step 1: Identify the Pattern

Check your error timeline for clusters of 404 errors affecting multiple well-known files. If you see a burst of errors for ads.txt, security.txt, and similar files within a short window, that’s the signature of a WebPageTest scan.

Step 2: Filter Bot Traffic

In TrackJS, you can filter out WebPageTest bot errors using ignore rules. If you don’t use TrackJS, you can filter it by user-agent string, but it’s not at all obvious that this is webpagetest:

Mozilla/5.0 (Linux; Android 8.1.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36 PTST/251202.154650

You can also use the broader bot filtering approach described in our search engine spiders and bots guide.

Step 3: Consider Which Files You Actually Need

Before dismissing all these 404s, consider whether you should implement any of these files:

Recommended for most sites:

  • /.well-known/security.txt - Helps security researchers contact you responsibly

Required for specific use cases:

  • ads.txt / sellers.json - If you run programmatic advertising
  • /.well-known/apple-app-site-association - If you have an iOS app with Universal Links
  • /.well-known/assetlinks.json - If you have an Android app with App Links

Optional but useful:

  • /.well-known/change-password - Helps password managers direct users to your password reset page
  • /.well-known/gpc.json - If you want to honor Global Privacy Control signals

For most websites, the majority of these files aren’t needed, and 404 responses are perfectly appropriate.

When to Ignore These Errors

WebPageTest bot errors should almost always be ignored because:

  • Bot traffic: These requests come from automated testing, not real users
  • No user impact: Your actual visitors aren’t requesting these files
  • Correct behavior: Returning 404 for files you don’t have is the right response
  • Temporary spikes: Bot scans produce bursts of errors, not ongoing issues

However, pay attention if:

  • Errors persist continuously: Might indicate something other than WebPageTest
  • You need these files: If you run ads or have mobile apps, missing files could be a real problem
  • Patterns change: Sudden increases might indicate new scanning activity worth understanding

Summary

WebPageTest.org bot errors appear in your JavaScript monitoring because WebPageTest uses real Chromium browsers that execute your tracking scripts. The bot probes for standard configuration files like ads.txt, security.txt, and various /.well-known/ paths. When these files don’t exist, you see 404 errors in your monitoring.

The fix is straightforward: filter WebPageTest bot traffic from your error monitoring using user agent or URL-based ignore rules. These errors don’t affect your users and don’t indicate problems with your site.

Remember: Bots using real browsers are increasingly common. When you see bursts of 404 errors for well-known configuration files, check the user agent before assuming something is broken.

]]>
How to fix `Cannot read properties of null (reading 'length')` https://trackjs.com/javascript-errors/cannot-read-properties-of-null-reading-length/ https://trackjs.com/javascript-errors/cannot-read-properties-of-null-reading-length/ Thu, 18 Dec 2025 00:00:00 +0000 How to fix `Cannot read properties of null (reading 'length')`

This is one of the most common JavaScript errors in web development. The error occurs when your code attempts to access the length property on a variable that is null. Unlike similar errors involving undefined, a null value typically indicates that a DOM API or storage operation couldn’t find what you were looking for.

The length property is commonly used with arrays and strings, making this error frequent in DOM manipulation, form handling, and data processing. The fact that the value is null (rather than undefined) tells you something specific: an API explicitly returned “nothing found” rather than the variable never being assigned.

Cannot read properties of null (reading ‘length’) is one of the most common JavaScript errors. In older browsers, you might see this displayed as: “Cannot read property ‘length’ of null”

Other browsers report this error differently:

  • Safari: null is not an object (evaluating '...length')
  • Firefox: TypeError: ... is null

If you’re seeing undefined instead of null, the cause is different:

The Problem

“Cannot read properties of null (reading ‘length’)” is a TypeError that occurs when JavaScript attempts to access the length property on a value that is null. This is a blocking error that stops script execution at the point where it occurs.

The error typically happens when a DOM API can’t find an element:


// Element doesn't exist in the DOM
const element = document.getElementById('nonexistent');
console.log(element.innerHTML.length);
// TypeError: Cannot read properties of null (reading 'length')

Key point: The value being null (not undefined) tells you that some API or operation explicitly returned “not found” or “no value.” This is an important clue for debugging.

Understanding the Root Cause

The distinction between null and undefined matters here. When you see null, it usually means:

  • A DOM method couldn’t find the element you requested
  • A storage API doesn’t have the key you asked for
  • A regex or string operation found no match
  • Code explicitly set a value to null

1. DOM Element Selection Failures

Most common cause: Methods like getElementById(), querySelector(), and closest() return null when they can’t find a matching element.

How to identify: Errors occur when your code runs before the DOM is ready, when element IDs have typos, or when elements are conditionally rendered and not yet present.

2. localStorage and sessionStorage Operations

The getItem() method returns null when the requested key doesn’t exist in storage.

How to identify: Errors occur when accessing stored data that was never set, was cleared, or the key name has a typo.

3. String and RegExp Match Operations

String.match() and RegExp.exec() return null when no match is found, not an empty array.

How to identify: Errors occur when processing text that doesn’t contain the expected pattern.

4. Detached or Removed DOM Elements

Properties like parentNode or parentElement return null for elements that have been removed from the DOM or were never attached.

How to identify: Errors occur after DOM manipulation operations or when working with dynamically created elements.

5. Explicit Null Assignment

Code may explicitly set variables to null as a placeholder or to indicate “no value,” and later operations forget to check for this.

How to identify: Review code flow to find where null values are intentionally assigned.

6. API Responses with Null Fields

Server responses may include null values for optional fields, which then get accessed without checking.

How to identify: Errors correlate with API calls and occur when processing response data.

How to Fix “Cannot read properties of null (reading ‘length’)”

Quick Troubleshooting Checklist

  • Check if DOM elements exist before accessing their properties
  • Verify element IDs and selectors match your HTML exactly
  • Ensure scripts run after the DOM is ready
  • Check localStorage keys for typos
  • Handle regex/match results that may be null
  • Add null checks before accessing length on dynamic values

If basic checks don’t resolve the issue, follow these systematic debugging steps:

Step 1: Identify What’s Returning Null

Use browser DevTools to pinpoint exactly which operation is returning null:

  1. Open DevTools Console (F12 or option-command-i)
  2. Look at the error stack trace to find the exact line
  3. Add a breakpoint or console.log before the problematic code
  4. Inspect which variable is null

// Debug by checking what's null
const element = document.getElementById('myElement');
console.log('Element found:', element); // null means element doesn't exist

const storedData = localStorage.getItem('myKey');
console.log('Stored data:', storedData); // null means key doesn't exist

const matches = someString.match(/pattern/);
console.log('Matches:', matches); // null means no match found

Step 2: Handle DOM Selection Safely

Always verify DOM elements exist before accessing their properties:


// Safe DOM element access
const element = document.getElementById('myElement');
if (element) {
  const textLength = element.textContent.length;
  console.log('Text length:', textLength);
} else {
  console.warn('Element not found: myElement');
}

// Using optional chaining (ES2020)
const textLength = document.getElementById('myElement')?.textContent?.length ?? 0;

// For querySelector with arrays
const items = document.querySelectorAll('.item');
console.log('Found items:', items.length); // querySelectorAll never returns null

Step 3: Wait for DOM Ready

Ensure your scripts run after the DOM is fully loaded:


// Option 1: DOMContentLoaded event
document.addEventListener('DOMContentLoaded', function() {
  const element = document.getElementById('myElement');
  // Element will exist if it's in the HTML
});

// Option 2: Place scripts at end of body
// <script src="app.js"></script> just before </body>

// Option 3: Use defer attribute
// <script src="app.js" defer></script> in <head>

Step 4: Handle localStorage Safely

Always check for null when retrieving stored values:


// Safe localStorage access
function getStoredArray(key) {
  const stored = localStorage.getItem(key);

  if (stored === null) {
    console.log(`No data found for key: ${key}`);
    return [];
  }

  try {
    return JSON.parse(stored);
  } catch (e) {
    console.error('Failed to parse stored data:', e);
    return [];
  }
}

// Usage
const items = getStoredArray('myItems');
console.log('Item count:', items.length); // Safe, always an array

Step 5: Handle Regex and String Matching

String.match() returns null when there’s no match, not an empty array:


// Unsafe: match() returns null when no match
const text = 'Hello world';
const matches = text.match(/\d+/); // No digits in string
console.log(matches.length); // TypeError: Cannot read properties of null

// Safe: Check for null first
const matches = text.match(/\d+/);
if (matches) {
  console.log('Found matches:', matches.length);
} else {
  console.log('No matches found');
}

// Alternative: Use nullish coalescing
const matchCount = text.match(/\d+/)?.length ?? 0;

Step 6: Use Defensive Programming Patterns

Build functions that handle null values gracefully:


// Defensive function for getting element text length
function getElementTextLength(selector) {
  const element = document.querySelector(selector);

  if (!element) {
    console.warn(`Element not found: ${selector}`);
    return 0;
  }

  return element.textContent?.length ?? 0;
}

// Defensive function for array-like operations
function safeLength(value) {
  if (value === null || value === undefined) {
    return 0;
  }

  if (typeof value === 'string' || Array.isArray(value)) {
    return value.length;
  }

  return 0;
}

// Usage
console.log(safeLength(document.getElementById('missing')?.textContent)); // 0
console.log(safeLength('hello')); // 5
console.log(safeLength(null)); // 0

Step 7: Monitor Null Property Errors in Production

These errors often only appear in production where real users encounter edge cases you didn’t anticipate. Set up monitoring to catch and understand these errors as they happen. Error monitoring services like TrackJS can help you identify patterns in null property access errors, showing you which DOM selectors fail, which localStorage keys are missing, and which user flows trigger the errors.

Monitor for trends like increases in these errors after deployments, which might indicate broken selectors or missing elements.

When to Ignore This Error

“Cannot read properties of null (reading ‘length’)” should rarely be ignored, as it usually indicates a real problem. However, consider the context:

  • Browser extension interference: Extensions modifying your DOM can cause elements to disappear
  • Race conditions in SPAs: Framework routing may cause brief periods where elements don’t exist
  • Third-party widgets: External code accessing elements it expects but your page doesn’t have

Investigate further when you see:

  • Consistent patterns: Same selector failing repeatedly
  • User-facing impact: Errors breaking core functionality
  • Production spikes: Sudden increases after deployments
  • Critical user flows: Errors affecting forms, checkout, or navigation

Summary

“Cannot read properties of null (reading ‘length’)” tells you that a value you expected to be a string or array is actually null. The null value (rather than undefined) is an important clue: it usually means a DOM API couldn’t find an element, localStorage doesn’t have the key you requested, or a regex found no match.

The solution involves understanding which operation returned null and adding appropriate checks before accessing the length property. Focus on the APIs that commonly return null (getElementById, querySelector, localStorage.getItem, String.match) and ensure your code handles the “not found” case gracefully.

Remember: null means “intentionally no value” while undefined means “never assigned.” This distinction helps you trace back to the source of the problem.

]]>
How to fix `Invalid or unexpected token` https://trackjs.com/javascript-errors/invalid-or-unexpected-token/ https://trackjs.com/javascript-errors/invalid-or-unexpected-token/ Wed, 10 Dec 2025 00:00:00 +0000 How to fix `Invalid or unexpected token`

This cryptic error means the JavaScript parser encountered a character it doesn’t recognize as valid JavaScript at all. Unlike “Unexpected token X” (where X is a recognizable character like < or }), “Invalid or unexpected token” signals that the lexer can’t even categorize what it’s looking at.

The frustrating part? The problematic character is often invisible. Smart quotes copied from Word documents, zero-width spaces from Slack, or byte order marks (BOM) from Windows editors can all trigger this error while looking perfectly normal in your code editor.

The Problem

“Invalid or unexpected token” is a SyntaxError that occurs during the lexical analysis phase, before your code even begins executing. The parser hit something it cannot process as a valid JavaScript token.

The error typically happens in scenarios like this: ​

// This looks fine but contains a smart quote from copy-paste
const message = “Hello world“  // ← The “ is actually Unicode U+201C

// This has an invisible zero-width space before the variable name
const ​userName = "Alice";  // ← Zero-width space before 'userName'

Key point: The error message often points to where parsing failed, not where the actual problem character exists. An unclosed string on line 10 might report an error on line 50.

“Invalid” vs “Unexpected” Token

These two error types indicate different problems:

“Invalid or unexpected token” means the lexer doesn’t recognize the character at all. Common with invisible Unicode characters, smart quotes, or BOM markers.

“Unexpected token X” (where X is ., <, }, etc.) means the lexer recognized a valid JavaScript token, but it appeared somewhere grammatically illegal. The character is fine, the placement isn’t.

// Invalid or unexpected token - lexer doesn't know what " is
const x = "hello"    // Smart quote, not a valid JS character

// Unexpected token . - lexer knows what . is, wrong placement
const x = ..5        // Double dot, valid character, wrong context

// Unexpected token , - lexer knows comma, wrong placement
const y = {,}        // Comma with nothing before it

Understanding the Root Cause

“Invalid or unexpected token” can stem from several sources, with invisible characters being the most common and frustrating:

1. Smart Quotes from Copy-Paste

Most common cause: Word processors automatically convert straight quotes (") to typographic “smart quotes” (""), which are entirely different Unicode characters that JavaScript doesn’t recognize as string delimiters.

Pasting code from Microsoft Word, Google Docs, Notion, or even Slack often introduces U+201C/U+201D (curly double quotes) or U+2018/U+2019 (curly single quotes).

How to identify: The error occurs immediately after pasting code from a document or chat application.

2. Byte Order Mark (BOM) Characters

The BOM, encoded as EF BB BF in UTF-8, frequently infiltrates files saved by Windows applications. When present at a file’s start, Node.js reports the strange character sequence  representing the invisible BOM.

How to identify: Errors occur at the very beginning of files, especially files edited on Windows or received from external sources.

3. Zero-Width Characters

Copying code from websites, PDFs, or chat applications often includes invisible Unicode characters like U+200B (Zero Width Space), U+200C (Zero Width Non-Joiner), or U+2028 (Line Separator). The line separator is particularly tricky because it’s valid in most contexts but illegal inside JavaScript string literals.

How to identify: The error points to a line that looks completely normal, with no visible issues.

4. File Encoding Mismatches

A file saved as UTF-16 but read as UTF-8 produces garbage bytes the parser cannot interpret. This commonly happens when files move between systems with different default encodings.

How to identify: Multiple strange characters appear in error messages, or errors occur immediately when loading files from different operating systems.

5. Corrupted Files or Partial Downloads

Incomplete file transfers, git merge conflicts that weren’t resolved, or text editors that crashed during save can leave files with corrupted or partial content.

How to identify: Errors started after a file transfer, git operation, or unexpected editor closure.

How to Fix “Invalid or unexpected token”

Quick Troubleshooting Checklist

  • Check if you recently copy-pasted code from a document or website
  • Look for the BOM character at the start of files
  • Use a hex editor or command-line tools to find invisible characters
  • Retype the problematic line manually instead of copying
  • Check file encoding settings in your editor
  • Test if the error persists after removing and retyping quotes

If basic checks don’t resolve the issue, follow these systematic debugging steps:

Step 1: Find Invisible Characters with Command-Line Tools

Use terminal commands to reveal hidden characters:

# Show invisible characters (tabs as ^I, non-printables as ^M)
cat -A file.js

# Hex dump to see exact byte values
xxd file.js | head -20

# Check file encoding and BOM presence
file file.js

# Check specifically for BOM bytes (ef bb bf)
head -c 3 file.js | xxd

Step 2: Configure Your Editor to Show Invisible Characters

VS Code can highlight dangerous invisible characters with the right settings:

// VS Code settings.json
{
  "editor.renderWhitespace": "all",
  "editor.renderControlCharacters": true,
  "files.encoding": "utf8",
  "files.eol": "\n"
}

The Gremlins extension for VS Code provides even better detection, highlighting dangerous invisible characters with red indicators in the gutter.

Step 3: Clean Up Copy-Pasted Code

When you’ve pasted code from external sources, this cleanup function handles common substitutions:

// Clean common copy-paste character problems
function cleanCopiedCode(text) {
  return text
    .replace(/[\u2018\u2019]/g, "'")   // Curly single quotes → straight
    .replace(/[\u201C\u201D]/g, '"')   // Curly double quotes → straight
    .replace(/\u2014/g, '--')          // Em-dash → double hyphen
    .replace(/\u00A0/g, ' ')           // Non-breaking space → regular space
    .replace(/^\uFEFF/, '');           // Remove BOM at start
}

The safest practice is always using Paste as Plain Text (Ctrl+Shift+V or Cmd+Shift+V) when copying code from any document or webpage.

Step 4: Remove the BOM from Files

If your file has a BOM character, remove it:

# Remove BOM from a file using sed
sed -i '1s/^\xEF\xBB\xBF//' file.js

# Or use Node.js to strip it
node -e "const fs = require('fs'); let c = fs.readFileSync('file.js', 'utf8'); fs.writeFileSync('file.js', c.replace(/^\uFEFF/, ''));"

Step 5: Use Binary Search to Isolate the Problem

When the error message doesn’t point to the actual problem, use the binary search method:

  1. Comment out half the file
  2. Check if the error persists
  3. If it does, the problem is in the remaining code
  4. If it doesn’t, the problem is in the commented section
  5. Repeat until you isolate the problematic line

Step 6: Just Retype It

When all else fails, manually retype the problematic line. This is often faster than hunting for invisible characters, and guarantees you’re using only valid characters.

Step 7: Monitor for Patterns in Production

Syntax errors like this shouldn’t reach production, but encoding issues can slip through when files are processed by different systems. Error monitoring services like TrackJS help you identify if users are encountering these errors, which might indicate build pipeline problems or file serving issues.

Track whether these errors correlate with specific browsers, operating systems, or geographic regions where different default encodings might be in play.

When to Ignore This Error

“Invalid or unexpected token” should never be ignored because it prevents code from running at all. However, consider the source:

  • Build tool output: The error might be in generated code, requiring you to fix the source file or build configuration
  • Third-party scripts: External scripts with encoding issues are outside your control but should still be reported to the vendor
  • Development only: If errors only occur in development, check your local file encoding settings

Investigate immediately when you see:

  • Production errors: Any syntax error reaching users indicates a serious build or deployment problem
  • After deployments: New encoding issues might have been introduced
  • Specific file patterns: Certain files or directories consistently causing problems

If you’re seeing similar parsing errors, these might be relevant:

Summary

“Invalid or unexpected token” means the JavaScript parser encountered something it can’t recognize as valid syntax, typically an invisible Unicode character, smart quote, or BOM marker. Unlike “Unexpected token X” errors (where the parser knows what the character is but can’t use it there), this error indicates truly unrecognizable input.

The solution usually involves finding and removing invisible characters using command-line tools or editor extensions, cleaning up copy-pasted code, and ensuring consistent file encoding across your development environment. When in doubt, manually retype the problematic line.

Prevention is more effective than debugging. Configure your editor to show invisible characters, always paste as plain text when copying code from external sources, and use EditorConfig or similar tools to enforce consistent encoding across your team.

]]>