Common JavaScript Errors Explained and How to Fix Them


JavaScript errors occur when your code does something incorrect that the language doesn’t support. They range from simple undefined variable references, through to more complex errors relating to async code and object scopes.
Understanding different error types and their causes enables you to anticipate where problems could occur. Writing bug-free code first time around means you can increase development velocity and spend less time chasing issues. In this guide, we’ll cover some of the most common JavaScript errors and share best practices for debugging them.
Debugging Common JavaScript Errors
JavaScript errors can be frustrating to troubleshoot. New developers may be unsure what a specific error actually means, especially if it’s being triggered from deep inside affected code. Experienced developers can easily encounter problems too, whether due to typos or incorrect use of advanced coding patterns.
Effective JavaScript error debugging and prevention requires you to accurately analyze the causes of problems. Beyond understanding different JavaScript error types, it’s useful to try intelligent tools like Qodo Gen that can explain errors for you. Qodo Gen lets you copy error messages into a chat conversation, then receive a contextually relevant response. Qodo’s AI agent understands your project’s code so it can provide a precise explanation of why each error is occurring.
Let’s now explore some of the top JavaScript error types.
Syntax Errors
Code written without the correct syntax will trigger syntax errors, which are among the most basic and common JavaScript errors.
Here are some examples.
Function with Missing Opening Curly Brace
Error
// SyntaxError: Unexpected token 'return' function sum(a, b) return (a + b); }
Correction
function sum(a, b) { return (a + b); }
Missing Mathematical Operator
Error
// SyntaxError: Unexpected token 'return' function sum(a, b) return (a b); }
Correction
function sum(a, b) { return (a + b); }
If Condition With Missing Closing Parentheses
Error
// SyntaxError: Unexpected token '{' if (test { console.log("OK"); }
Correction
if (test) { console.log("OK"); }
Fixing JavaScript Syntax Errors
Syntax errors are frequently caused by parentheses, curly braces, and mathematical operators that are missing or positioned incorrectly. These errors typically result in large blocks of code being incorrectly formatted. The error message will usually point to the line of code where the issue is present, but it may not always be accurate. To fix these errors, carefully examine your code for these types of mistakes and add the necessary characters in the appropriate places.
Type Errors
JavaScript type errors occur when you perform an unsupported operation on a value:
// TypeError: User.username is not a function const User = { username: "Demo" }; User.username();
In this example, the user
object has a property called username
, but it’s set to a string, not a function. As a result, a TypeError
is thrown when the code tries to call username()
as a function.
To fix type errors, begin by verifying that you are accessing the correct variable. Next, examine where the variable is assigned to confirm that it always has the expected type. IDEs and static analysis engines can also identify these issues.
Reference Errors Caused by Undefined Values
You’ll see a JavaScript Reference Error when you try to access a variable that hasn’t been defined:
// ReferenceError: User is not defined console.log(User.username);
This code doesn’t set the User
variable so a ReferenceError
occurs when it’s accessed. To fix the problem, make sure the variable is always defined by the time you use it:
const User = { Username: "demo" }; console.log(User.username);
This problem often occurs when a variable is set conditionally in an if
statement. If you’ll use the variable after the if
statement, make sure you assign it a default value either outside the block or within an else
statement.
Range Errors
Some JavaScript operations involving iteration, recursion, arrays, and mathematics throw a RangeError
when you try to access an invalid position. It means the position is out of range because no value has been assigned.
Trying to create an array with a negative length will always trigger a RangeError
:
// RangeError: Invalid array length const arr = new Array(-5);
Similarly, a RangeError
is thrown when you pass an invalid value to mathematical functions like toFixed()
:
// RangeError: toFixed() digits argument must be between 0 and 100 const pi = 3.54; console.log(pi.toFixed(-100));
The best way to fix range errors is to modify your code to always keep values within the valid range. Make sure to check values from user input and, if they could result in a range error, reset them to fall within the allowed bounds. The following example demonstrates a safe way to handle this.
const numberOfUsers = getNumberOfUsers(); if (isNaN(numberOfUsers) || (numberOfUsers < 0)) { numberOfUsers = 0; } // No RangeError because numberOfUsers is always a valid argument const usersArray = new Array(numberOfUsers);
Scope Errors
You should correctly scope each of your variables to avoid unexpected errors. In JavaScript, variables can be declared using let, var, or as a global variable. It is essential to understand the scope associated with each declaration to prevent errors.
let
declares a block-scoped variable, accessible only within the nearest enclosing block (defined by curly braces{}
).var
declares a function-scoped variable, accessible throughout the entire function where it is declared- Global variables are declared by omitting
let
,var
, orconst
, or by usingvar
outside a function at the global scope. These variables can be accessed from anywhere within your script. - Mishandling variable scope can lead to unexpected behavior. Ensure that you declare your variables with the appropriate scope for your use case.
You should correctly scope each of your variables to avoid unexpected errors:
var message = "nothing to report"; function sayHello(user) { message = "Hello"; console.log(message); } // nothing to report console.log(message); // Hello sayHello(); // Hello console.log(message);
The example above demonstrates the pitfalls of scope. The sayHello()
function doesn’t apply var
or let
to its message
variable, so the value assignment applies to the variable defined in the global scope. The issue can be fixed by correctly scoping the variable within the function body:
var message = "nothing to report"; function sayHello(user) { var message = "Hello"; console.log(message); } // nothing to report console.log(message); // Hello sayHello(); // nothing to report console.log(message);
Now a new variable is defined within the function’s scope.
This Errors
JavaScript’s this
keyword refers to the context in which the code is running. It can vary depending on how a function is called. This often causes problems when this doesn’t reference the context you expect it to:
const User = { username: "demo", getUsername() { return this.username; } } const getUsername = User.getUsername; // undefined console.log(getUsername());
This example displays undefined
because the getUsername()
function is being called in the context of the global scope, not the User
object. You can solve the problem by either calling the method directly on User
, or explicitly binding it to the User
object.
// Option 1 // demo console.log(User.getUsername()); // Option 2 // demo console.log(getUsername.call(User)); // Option 3 const getUsername = User.getUsername.bind(this); // demo console.log(getUsername());
Strict Mode Errors
JavaScript’s strict mode helps eliminate common errors by forbidding potentially problematic operations. It enforces best practices and prevents use of syntax that’s known to cause issues.
You can opt into strict mode at the function or file level:
"use strict"; function myFunction() { "use strict"; }
There’s significant variations in JavaScript’s behavior depending on whether strict mode is enabled for a particular line. Because it’s possible to mix and match strict and non-strict functions and files, this can lead to confusing results when errors are either thrown or silenced unexpectedly.
function functionOne() { // Assigning to an undefined variable is forbidden in strict mode message = "demo"; } function functionTwo() { "use strict"; functionOne(); } functionTwo();
The code above runs successfully: functionOne()
performs an operation that’s forbidden in strict mode, but strict mode isn’t actually enabled for the function. Although functionTwo()
does enable strict mode, this only applies to its own code—not the code in functionOne()
. It’s therefore critical that strict mode is correctly enabled for relevant files and functions. For the most predictable results, try to apply it across your entire application.
Event Handling Errors
JavaScript’s browser event handling system can hide subtle errors that prevent your handlers from working correctly.
The first common problem is trying to access an event’s details without explicitly declaring an event
argument in your handler:
document.addEventListener("click", () => { // ReferenceError: event is not defined console.log(event.target.getAttribute("name")); });
This code still works in many browsers, but is deprecated and can cause confusion. It’s good practice to explicitly define an event
argument instead:
document.addEventListener("click", event => { // ReferenceError: event is not defined console.log(event.target.getAttribute("name")); });
Event handlers can also suffer from scope confusion, especially when combined with modern JavaScript syntax features like arrow functions:
document.addEventListener("click", () => { // TypeError: this.getAttribute is not a function console.log(this.getAttribute("name")); });
The this
keyword normally refers to the DOM element that the event was fired on, but this doesn’t apply to arrow functions. They don’t create their own scope, so this actually refers to the outside scope surrounding the arrow function’s definition. You can fix this by switching to a regular function definition:
document.addEventListener("click", function () { // Works OK (this is the element that fired the event) console.log(this.getAttribute("name")); });
Many event handling problems stem from unwanted event propagation. By default, events “bubble up” through the DOM elements above the element on which they’re fired. If you’ve registered multiple event handlers in the DOM tree, then you could find that several handlers fire for one event. Use the event object’s stopPropagation()
method to confine the event and stop it bubbling up beyond the current handler.
document.addEventListener("click", event => { // Parent elements won't be notified of the event event.stopPropagation(); });
Circular References and Internal Recursion Errors
JavaScript code often involves object structures, closures, and functional programming methods that can be susceptible to circular references. This may lead to infinite internal recursion that ends with a RangeError
or InternalError
, depending on the JavaScript engine being used.
If a function calls itself, or a property in an object refers back to the same object, then you should ensure there’s an escape mechanism that prevents the recursion descending too far. JavaScript monitors the size of the function call stack and stops your script if it becomes too deep (the limit varies by engine).
Recursion errors can also crop up when you perform operations on an array that includes itself:
class User { constructor(username) { this.username = username; } } const users = []; users.push(new User("demo")); users.push(users); const listUsernames = () => { for (const user of users) { if (Array.isArray(user)) { listUsernames(user); } else console.log(user.username); } }; listUsernames();
Here, the nested loop never terminates because the user
variable continually refers back to the same array.
Unexpected Results from Async Functions
Asynchronous code can be particularly susceptible to errors. The most common mistake is forgetting to use the await
keyword when calling an async
function:
async function fetchUsers() { // ... } const users = fetchUsers(); // Promise { <pending> } console.log(users);
Because this code doesn’t use await
, JavaScript won’t wait for the fetchUsers()
call to complete. The value of users
won’t resolve to the final value, causing unexpected behavior in your app. You can fix this by ensuring all async
functions are called with await:
async function fetchUsers() { // ... } const users = await fetchUsers(); // Final value console.log(users);
Unhandled Promise Rejection Errors
UnhandledPromiseRejection
errors occur when a Promise without a .catch()
handler calls reject()
to report an error:
// UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch() const fetchUsers = new Promise((resolve, reject) => { reject("Not implemented"); });
To fix this error, you must chain a .catch()
handler onto the Promise. Your function will receive the thrown error as an argument, allowing your script to gracefully recover from the problem.
// "An error occurred" const fetchUsers = new Promise((resolve, reject) => { reject("Not implemented"); }).catch(e => { console.error("An error occurred."); });
Use of Reserved Identifiers
JavaScript reserves several keywords. Reserved keywords cannot be used to name variables, functions, classes, and other code entities.
Some of these words—like case
, class
, import
, new
, and package
—are often good fits for values used within an application. However, trying to use them as a JavaScript name will trigger a SyntaxError
. The JavaScript engine will still try to interpret them as language features:
const ImportFactory = () => { return {importType: "demo"}; }; // SyntaxError: Unexpected token 'import' const import = ImportFactory();
You can avoid these errors by simply ensuring you don’t use the reserved keywords to name any variables, functions, or classes. Static analysis engines and AI coding assistants can help reveal naming problems.
JavaScript Module Errors
JavaScript modules allow you to import code from other scripts. They’re supported in browsers and standalone engines like Node.js:
// lib.js const sum = (a, b) => (a + b); export {sum}; // main.js import {sum} from "./lib.js"; console.log(sum(3, 2));
Import Declarations May Only Appear at Top Level
Receiving an import declarations may only appear at top level of a module error
(or a similar message) is one of the most common module errors. It’s usually seen in browsers when you’ve forgotten to mark the script as a module when loading it in a <script>
tag:
<head> <!-- The script will not be loaded as a module --> <script src="main.js"></script> </head> You can correct the error by adding the type="module" attribute to your <script> tag: <head> <!-- The script will now be correctly loaded as a module --> <script src="main.js" type="module"></script> </head>
You can correct the error by adding the type="module"
attribute to your <script>
tag:
<head> <!-- The script will now be correctly loaded as a module --> <script src="main.js" type="module"></script> </head>
If you’re using Node.js, then you should rename your script to have a .mjs
file extension. Alternatively, you can set "type": "module"
in your package.json
file instead.
This error can also occur if you try to use the import
statement within a nested function or code block.
Requested Module Does Not Provide an Export Named …
Another common module error is the requested module <module> does not provide an export named <export>:
// SyntaxError: The requested module './lib.js' does not provide an export named 'subtract' import {subtract} from "./lib.js"; console.log(subtract(3, 2));
The error’s thrown in this example because lib.js
doesn’t export anything called subtract
. Check your import
statement for typos, then ensure the item you’re trying to import is specified in the module’s export
statement.
Export … Is Not Defined in Module
This error is the counterpart to the previous one discussed above. It occurs when an export
statement references an undefined variable:
const sum = (a, b) => (a + b); export {sum, subtract};
There’s no variable called subtract
in the scope so it can’t be exported from the module.
How to Prevent JavaScript Errors?
While coding JavaScript programs, encountering bugs and errors is unavoidable. However, many of these can be prevented by adopting safe programming practices and utilizing modern tools for early detection of potential issues during code development.
Write Comprehensive Unit Test Suites
Unit tests ensure your code functions as intended before going live, allowing for rapid iteration on isolated units of logic and verifying that everything remains functional after future updates. Though writing tests can be time-consuming and tedious, generative AI tools like Qodo Gen automate the process by intelligently analyzing your project to understand how the code operates.
Use Linters and Static Analysis
JavaScript linters and static analysis tools like ESLint and Semgrep scan your code without running it. They report bugs, errors, and security vulnerabilities, providing instant feedback to developers.
Use Linters and Static Analysis
Generative AI lets developers automate code production, eliminating errors caused by typos and human mistakes. It ensures best practices are followed so your code works more efficiently and contains fewer bugs.
Qodo Gen is an IDE extension that provides advanced context-aware gen AI code completion and chat. Adopting Qodo enables you to rapidly generate new code that’s tailored to your project and fully aligned with your team’s code quality standards.
Use Generative AI to Rapidly Produce Effective Error-Free Code
Using TypeScript instead of JavaScript also prevents many common errors by enforcing strict type safety. TypeScript code looks like JavaScript and compiles back to it, but supports advanced syntax including interfaces, enums, decorators, and mixins. It can make larger JavaScript applications much more maintainable.
Summary
JavaScript applications are susceptible to errors because the language is loosely typed and doesn’t enforce strict practices by default. Moreover, its object model can create confusing situations with the this
keyword, while many modern JavaScript programs also rely on asynchronous code that has unique error handling requirements.
In this article, we’ve discussed some of the most common causes of JavaScript errors, but this is far from being an exhaustive guide. You can keep your JavaScript projects free of errors by using Qodo Gen to intelligently generate error-free code and tests that match your project’s standards. Book a demo or get started for free.