How to Prevent the Error: Cannot Read Property '0' of Undefined

updated on 30 November 2022

Errors and crashes in software development can be frustrating, especially if they're difficult to debug. In JavaScript, errors relating to undefined are some of the simplest and most common. One such error is the Cannot read property '0' of undefined error.

In this article, you'll learn about the Cannot read property of '0' of undefined error, when it can occur, and how to mitigate it.

What is the Cannot Read Property '0' of Undefined Error

undefined is a special value in JavaScript that is used to indicate the absence of a value. When you declare a variable but assign it no value, it's automatically initialized to undefined, and trying to access properties of undefined results in an error. The Cannot read property xxx of undefined error is one such example. In particular, the Cannot read property 'n' of undefined (where n is a number) usually occurs when you try to access elements of an array that are undefined.

In JavaScript, an array is a special type of object, and just like an object, you can obtain or set its properties using the . or [] operators:

let arr = [1,2,3]
console.log(arr.length) // 3
console.log(arr['length']) // 3

However, the elements of the array become properties themselves. In the same way that length is a property, the first element becomes the 0 property, the second element becomes the 1 property, and so on:

Logging an array to the console shows its object structure-t7bzv

This means accessing the first element with arr[0] is the same as accessing the 0 property of arr. If you try to access the first element of a variable that is supposed to be an array but is actually undefined, you will get the dreaded Cannot read property '0' of undefined error.

The same error can occur for array-like objects as well. Array-like objects are objects that support the subscript ([]) operator (ie a string).

Note that, depending on what JavaScript engine you're using, the error message may vary slightly.

Gecko (in Firefox) gives this error: TypeError: XXX is undefined. n older versions of the v8 engine (in Chrome, Chromium, and Node.js), the error message reads like this: Cannot read property '0' of undefined.  It was recently modified to read TypeError:Cannot read properties of undefined (reading '0') in newer versions of v8. This means that in modern Chrome, Chromium-based browsers, and Node.js, you should see TypeError: Cannot read properties of undefined (reading '0').

Leaving this error unchecked can be very detrimental to the user experience because it can lead to bugs or crashes. However, this error is easy to fix once you have pinpointed the exact reason your variable is undefined.

When Does the Cannot Read Property '0' of Undefined Error Occur

From the previous discussion, it's clear that the cause of the Cannot read property '0' of undefined error is the array being undefined. There are multiple reasons why an array can be undefined, including the following"

Forgetting to Initialize the Array

The most common mistake that leads to a Cannot read property 0 of undefined error is forgetting to initialize the array when declaring it. An uninitialized variable will be undefined by default, and trying to access its elements will throw the Cannot read property '0' of undefined error:

let arr // arr is undefined
console.log(arr[0]) // throws error

Accessing an Array through a Nonexistent Key

If a property of an object is an array, it's possible to accidentally misspell the key or try to access the array through the wrong key. A nonexistent key in an object returns undefined;  thus, trying to access its elements throws the Cannot read property '0' of undefined error.

For example, consider the following object where marks is an array:

let student = {
  name: "John",
  marks: [80, 90, 70]
};

You can access the marks array using student.marks or student["marks"]. But if you misspell "marks", it returns undefined:

console.log(student.mark); // undefined

If you're not careful and try to access the element of the array, you'll get the Cannot read property '0' of undefined error:

console.log(student.mark[0]); // throws error

Accessing a Wrong Index in a Multidimensional Array

In many languages, accessing past the bounds of an array throws an error. But in JavaScript, if you try to access an out-of-bound element of an array, you'll get undefined:

let arr = [1, 2, 3];
console.log(arr[3]); // undefined

This feature of JavaScript can cause problems when you're trying to access an element of a multidimensional array (ie an array of arrays):

let matrix = [
    [1,0,0],
    [0,1,0],
    [0,0,1],
    [0,0,0]
]

Here, matrix is an array of four elements, each of which is an array of three elements. You can access an individual entry by specifying the index of that entry with respect to both the outer and inner arrays.

For example, matrix[0][0] is the first entry of the first array:

console.log(matrix[0][0]); // 1

Essentially, matrix[0] returns the first array whose first entry is then accessed using [0]. If the first index is out of bounds, it returns undefined. Trying to further access its element throws an error:

console.log(matrix[5][0]); // throws error

In the earlier snippet, matrix[5] is undefined, so trying to access its element throws the Cannot read property '0' of undefined error.

Populating an Array from a Function

It's a common practice to populate an array by calling a function. However, you must make sure that the function is actually called. Not doing so may result in the array being undefined.

In the following example, the populate function is responsible for populating the array, but it's not being called. Thus, arr is undefined, and when arr[0] is accessed, it causes the Cannot read property '0' of undefined error:

let arr;

function populate() {
    arr = [1,2,3]
}

console.log(arr[0]) // throws error

Having Function Arguments That are Undefined

JavaScript is fairly lenient about function arguments. If a function accepts n number of arguments and you pass more than n arguments, the extra arguments are ignored. However, if you pass less than n arguments, the extra arguments are undefined, and trying to access them results in an error.

In the following code, the function foo accepts one argument, but none are passed, resulting in data being undefined and the Cannot read property '0' of undefined error:

function foo(data) {
    console.log(data[0])
}

foo()

Conditionally Assigning a Variable

A common pattern in programming is to assign a variable with a value that depends on some condition. In this case, care must be taken to ensure all the possible cases have been taken care of; otherwise, the variable may be undefined and cause errors.

Consider the following function that populates the marks array based on the name parameter:

function populate(name) {
    let marks

    if(name == "Bob") {
        marks = [88, 99, 100]
    } else if(name == "Alice") {
        marks = [78, 86, 98]
    }

    console.log(marks[0])
}

The function will work fine if the name is either Bob or Alice. However, any other name (or even misspelling Bob or Alice) will throw the Cannot read property '0' of undefined error:

populate("Alice") // Ok
populate("alice") // throws error

Creating a Race Condition

A very common case, especially when dealing with asynchronous processes, is falling victim to the so-called race condition. A race condition occurs when there's no definite order of completion of multiple processes, and depending on the order, undesirable things can happen.

For example, suppose you're populating an array in an async function. If you try to access the array before the array is populated, you'll get the Cannot read property '0' of undefined error:

let arr

async function populate() {
    setTimeout(() => arr = [1,2,3], 2000) // A timeout is used to simulate a long-running job
}

populate()
console.log(arr[0]) // throws error

In the previous code snippet, the populate function is an asynchronous function that populates the array. In the code, setTimeOut is used to simulate a delay of two seconds for the purpose of demonstration. In practice, this could be a delay caused by having an API call, reading from a file or a database, or waiting for user input. This means that the console.log statement is executed before the array is populated. At that point, the array is still undefined, and it throws the Cannot read property '0' of undefined error.

Changing API Responses

No matter what application you're developing, it's likely that you'll be depending on some external services. This could be an API, a database, a file, or a user input. Apart from the possibility of creating a race condition, these external services can also create problems if their structure changes.

If you're populating an array from the response of an API call, it can be undefined due to various reasons, including the following:

  • The API can return undefined if no data is available.
  • A poorly designed API can return undefined because of an error.
  • The API designers can change the structure of the response and remove or rename the fields, resulting in the original fields being undefined.

For example, consider the following API call where an array is populated from the items field of some API:

fetch(some_url)
.then(res => res.json())
.then(data => {
    let arr = data.items;
    console.log(arr[0]);
})

If the items field is undefined, the previous code will throw the Cannot read property '0' of undefined error. If a new version of the API removes the items field, accessing data.items will return undefined and accessing elements will throw the Cannot read property '0' of undefined error.

How to Mitigate the Cannot Read Property '0' of Undefined` Error

Unfortunately, there's no one-size-fits-all solution to mitigating the Cannot read property '0' of undefined error. However, the following are some of the ways you can try and prevent it:

Check your Code for Common Mistakes

Have you forgotten to initialize the array? Have you made a typo somewhere? Have you forgotten to call the function that populates the array? These are some of the common mistakes that can cause the Cannot read property '0' of undefined error.

It's a good idea to go through your code and try to look for these common mistakes. Most often, you'll find an issue in your code, and you can quickly fix it. You can also use a linter like ESLint to catch some very common errors, like misspelled variables or function names.

Check for Undefined Before Accessing the Array

To prevent the Cannot read property '0' of undefined error, you can check whether the array is undefined before accessing it, and there are multiple ways to do so.

A very common solution (that is highly recommended) is to check whether the type of a variable is undefined or not:

let arr
if (typeof arr !== "undefined") {
    console.log(arr[0]) // Will not run because arr is undefined
}

let arr2 = [1,2,3]
if (typeof arr2 !== "undefined") {
    console.log(arr2[0]) // Will run because arr2 is defined
}

However, the problem with this solution is that if a variable is nonexistent, typeof still returns undefined. While this might seem like a useful feature since it prevents you from accessing nonexistent variables, relying on this behavior is not a good practice. If you're trying to access a nonexistent variable, it indicates a bigger problem with your code, and you want to catch this error instead of suppressing it.

To solve this, you can check for undefined using the comparison operators:

let arr
if (arr !== undefined) {
    console.log(arr[0]) // Will not run because arr is undefined
}

If the variable is not defined, it will throw a ReferenceError:

if (scores !== undefined) {
    console.log(arr[0]) // throws ReferenceError
}

// Output:
// ReferenceError: scores is not defined

However, just because the variable is not undefined doesn't mean that you're safe. It could still be null. In addition, there's no guarantee that it's an array or that it has the index you're trying to search for. In this instance, the recommended practice is to use the Array.isArray method to check whether the variable is an array or not and check its length before accessing elements. As an added bonus, Array.isArray() also checks if the variable is undefined or null or not, and it throws a ReferenceError if it's not defined:

let arr
if (Array.isArray(arr) && arr.length > 0) {
    console.log(arr[0]) // Will not run because arr is undefined
}

let arr2 = [1,2,3]
if (Array.isArray(arr2) && arr2.length > 0) {
    console.log(arr2[0]) // Will run because arr2 is defined
}

let arr3 = []
if (Array.isArray(arr3) && arr3.length > 0) {
    console.log(arr3[0]) // Will not run because arr3 is empty
}

This method, however, does not work for array-like objects because they are not an Array. While there are methods that work for all arrays and array-like objects, they can be complicated. It's often easier to simply check for the particular array-like types that you're actually using.

This is the easiest solution to mitigate the Cannot read property '0' of undefined error. You can also use it to check if an argument of a function is undefined or not. However, this solution only prevents the code from crashing if an array is undefined, and it doesn't prevent the array from being undefined in the first place. If you have a lot of arrays, checking them every time you want to access them can be tedious and error-prone.

If you don't want to deal with type-checks at every location where an array is accessed, try TypeScript. TypeScript will type-check your variables for you and will simply refuse to compile if an array is undefined (unless you explicitly allow it to):

let arr: number[] = undefined
console.log(arr[0])

This code will refuse to compile with the error Type 'undefined' is not assignable to type 'number[]'.

Provide an Alternative Value When the Array is Undefined

Another strategy to make sure the array isn't undefined is to provide an alternative value when the array is undefined. You can use the logical OR (||) operator to retain the original value if the array isn't undefined or provide a default value if the array is undefined:

let arr
arr = arr || [1,2,3] // Assigns [1,2,3] because arr is undefined
console.log(arr[0])

This code can be shortened by using the ||= operator:

let arr
arr ||= [1,2,3] // Assigns [1,2,3] because arr is undefined
console.log(arr[0])

let arr2 = ["Hello", "World"]
arr2 ||= [1,2,3] // Retains the original value
console.log(arr2[0])

This method is useful if you have predefined default values to assign. For example, in an application, you may set some default configuration if the user doesn't provide any custom configuration.

Use Optional Chaining

The optional chaining operator (?.) is a safe way to access the properties of an object without throwing an error if the property is null or undefined. An expression involving the optional chaining operator returns undefined if any one reference is missing (null or undefined). This makes it easier to write code because you don't have to check for undefined or null at every level:

let obj = {
    name: "John",
    age: 30,
    marks: {
        english: [90, 99, 100]
    }
}

// without optional chaining
if(obj && obj.marks && obj.marks.english) {
    console.log(obj.marks.english[0])
}

// With optional chaining
console.log(obj?.marks?.english?.[0])

console.log(obj?.scores?.english?.[0]) // Will return undefined because scores is undefined but won't throw error

This method works best in cases where the structure of the object isn't guaranteed; for example, in an API response.

Use Const Whenever Possible

Using const instead of let or var is a good practice. It's also a good idea to use const whenever you're dealing with a variable that you don't intend to change. A const variable also forces you to assign an initial value to it so that it's never unintentionally undefined:

const arr; // Not allowed
const arr2 = []; // Allowed

It will also make sure an array doesn't accidentally (or intentionally) get assigned undefined during the lifecycle of the code:

const obj = { name: "John", marks: [1,2,3] }
const arr = [1,2,3]
arr = obj.scores // Not allowed

Using const, however, means that you lose some of the flexibility of let and var. For instance, it's not possible to populate an array by assigning a value to it. The following code will not work with const:

const arr
arr = [10, 20, 30] // Not allowed

You can work around it by initially declaring the variable as an empty array and later adding elements to it:

const arr = []
arr.push([1,2,3])
console.log(arr) // [1,2,3]

This works because while const doesn't let you reassign the array, it lets you modify the elements of the array.

If you need to reassign the array in multiple places, it's better to use let, as it becomes cumbersome with const. However, if you're sure you won't modify the array, choose const.

Use Async/Await and Promises Correctly

To prevent race conditions between background processes, pay extra attention to the order in which the processes are executed. To make sure an array isn't accessed before it's initialized, use async/await and promises:

let arr

async function populate() {
    // Some long-running job
    // ...
    arr = [1,2,3]
}

async function access() {
    console.log(arr[0])
}

In the previous code, you must wait for the populate() function to finish executing before you can use the access() function to access the array:

// Using await
await populate()
access()

// Using promise
populate().then(access)

Use a Try…Catch Block

The try...catch block is used to catch run-time errors or exceptions. If the code inside the try block throws an error, the error is passed to the catch block for handling. If no error is thrown, the catch block will not be called:

try {
  let roles
  console.log(roles[0])
} catch (error) {
  console.log(error); // TypeError: Cannot read property '0' of undefined
}

Although using a try…catch prevents the error from crashing the application, it's merely hiding the symptom, not the cause. If your code is trying to access an array that is undefined, it indicates an issue somewhere else, and it's better to explicitly check for undefined using an if statement, as explained previously.

However, in cases where you want to halt the execution of the rest of the program upon encountering an error or you have multiple places in the code that can throw an error, try…catch is a good choice.

Conclusion

The Cannot read property '0' of undefined error can be avoided during software development by adopting some best practices. In this article, you learned why this error happens and how to mitigate it by always ensuring that the array you are trying to access is not undefined.

Meticulous can help you catch these bugs before they get to your users in production. Remember, you need to use the right approach for the right situation. This will help you write better code and reduce the risk of bugs.

Meticulous

Meticulous is a tool for software engineers to catch visual regressions in web applications without writing or maintaining UI tests.

Inject the Meticulous snippet onto production or staging and dev environments. This snippet records user sessions by collecting clickstream and network data. When you post a pull request, Meticulous selects a subset of recorded sessions which are relevant and simulates these against the frontend of your application. Meticulous takes screenshots at key points and detects any visual differences. It posts those diffs in a comment for you to inspect in a few seconds. Meticulous automatically updates the baseline images after you merge your PR. This eliminates the setup and maintenance burden of UI testing.

Meticulous isolates the frontend code by mocking out all network calls, using the previously recorded network responses. This means Meticulous never causes side effects and you don’t need a staging environment.

Learn more here.

Authored by Aniket Bhattacharyea

Read more