Fix Common Errors

Solve "Map Is Not a Function" Error

Fix 'map is not a function' errors in JavaScript and React. Understand array type mismatches, defensive coding patterns, and how to catch data shape bugs in production.

The "map is not a function" error is one of the most common runtime errors in JavaScript and React applications. It happens when you try to call .map() on something that isn't actually an array — usually a null value, undefined, or some other data type. In a fast-paced application where API responses vary or state updates don't align, you can ship code that works in development but breaks in production. Understanding why this error occurs and how to prevent it can save hours of debugging.

This error is particularly tricky in React applications because data shape often changes unexpectedly — an API might return null instead of an array, a prop might arrive before the initial fetch completes, or a third-party service might change its response format. Identifying the exact moment data shape changes in production is where proper error tracking becomes invaluable.

What Causes "Map Is Not a Function"

The root cause is always the same: calling .map() on a non-array value. Here are the most common scenarios:

// Calling map on null or undefined
const data = null;
const items = data.map(item => item.id); // TypeError: Cannot read property 'map' of null

// Calling map on a single object instead of an array
const user = { name: 'Alice', id: 1 };
const ids = user.map(u => u.id); // TypeError: user.map is not a function

// API returns an object wrapper instead of an array
const response = { data: [1, 2, 3], status: 'success' };
const numbers = response.map(n => n * 2); // TypeError: response.map is not a function

In React, this often surfaces when state hasn't been initialized correctly or when an API call hasn't completed yet:

function UserList() {
  const [users, setUsers] = useState(null); // Bug: initialized as null, not []
  
  useEffect(() => {
    fetchUsers().then(setUsers);
  }, []);
  
  // Crashes during first render because users is null, not an array
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

Defensive Coding: Check Your Types First

The simplest fix is to verify the data is actually an array before calling .map():

function UserList() {
  const [users, setUsers] = useState([]); // Initialize as empty array
  
  useEffect(() => {
    fetchUsers().then(setUsers);
  }, []);
  
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

Initialize state with the correct type from the start. If users should be an array, start it as an empty array, not null. This prevents the error during the initial render.

For API responses, check that you're accessing the right property:

async function loadUsers() {
  try {
    const response = await fetch('/api/users');
    const result = await response.json();
    
    // Ensure result.data is an array before mapping
    const users = Array.isArray(result.data) ? result.data : [];
    setUsers(users);
  } catch (error) {
    console.error('Failed to load users:', error);
  }
}

Use Array.isArray() for Type Safety

When you're unsure about the shape of incoming data, explicitly check it:

const data = getSomeData();

// Safe approach: verify it's an array
if (Array.isArray(data)) {
  return data.map(item => item.value);
} else {
  console.warn('Expected an array, got:', typeof data);
  return [];
}

This pattern is especially useful when working with third-party APIs or when props come from Redux or context:

function ProcessData({ items }) {
  if (!Array.isArray(items)) {
    return <p>No data available</p>;
  }
  
  return (
    <div>
      {items.map(item => <Item key={item.id} {...item} />)}
    </div>
  );
}

Handle Data Shape Changes in Production

Data type issues rarely show up in development because you're testing against a single, predictable backend or mock data. In production, APIs change, edge cases surface, and sometimes downstream services return unexpected structures.

API contracts are fragile. A backend team might wrap responses in an object, return null for empty results, or change field names — without updating all consumers. The error tracker you use must capture these moments so you can correlate type mismatches with code changes or deployment versions.

When a .map() error fires in the wild, you want to know:

  • What was the actual value that caused the error?
  • What was expected? (An array of users, products, etc.)
  • When did it start? (Correlated with a deploy, API change, or new traffic pattern?)
  • How many users hit it? (One? Thousands?)

Using an error tracker like LightTrace, you capture the full context: stack trace, local variables, tags, and breadcrumbs showing the sequence of events leading up to the error. This context is crucial for array type issues, where the root cause often lies in a previous step (an API call, a data transformation, or a state mutation).

Array Type Coercion Pitfalls

Sometimes the bug is subtler. You might think you have an array, but loose equality or partial data structures create false positives:

// Strings have a length property but are not iterable via map in the same way
const str = "hello";
// str.map(...) // TypeError: str.map is not a function
// But strings do NOT have a map method

// Numbers, booleans, and objects don't have map
const num = 42;
// num.map(...) // TypeError: num.map is not a function

// Check both type and structure
function safeMap(value) {
  if (Array.isArray(value)) {
    return value;
  } else if (value && typeof value === 'object') {
    // It's an object, possibly wrapping the array
    if (Array.isArray(value.data)) return value.data;
    if (Array.isArray(value.items)) return value.items;
    if (Array.isArray(value.results)) return value.results;
  }
  return []; // Default to empty array
}

// Usage
const users = safeMap(apiResponse);
users.map(user => console.log(user.name));

Real-World Scenario: The Silent Type Shift

Consider a typical React component that fetches a list from an API:

function Dashboard() {
  const [items, setItems] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetch('/api/items')
      .then(r => r.json())
      .then(data => {
        // Danger: assumes data is always an array
        setItems(data);
        setLoading(false);
      })
      .catch(err => {
        console.error(err);
        setLoading(false);
        // Items remains null on error!
      });
  }, []);
  
  if (loading) return <p>Loading...</p>;
  
  // Crashes if fetch failed and items is still null
  return <div>{items.map(item => <ItemCard key={item.id} {...item} />)}</div>;
}

The fix:

function Dashboard() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    fetch('/api/items')
      .then(r => r.json())
      .then(data => {
        const itemsArray = Array.isArray(data) ? data : [];
        setItems(itemsArray);
      })
      .catch(err => {
        setError(err);
        setItems([]); // Ensure items is always an array
      })
      .finally(() => setLoading(false));
  }, []);
  
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Failed to load items</p>;
  
  // Safe: items is always an array
  return <div>{items.map(item => <ItemCard key={item.id} {...item} />)}</div>;
}

Prevention Best Practices

Set up a linter rule like eslint-plugin-react to warn when .map() is called on something that might not be an array. Tools like TypeScript also catch this at compile time if you properly type your state and props.

  1. Always initialize state with the correct type. If it's a list, use useState([]), not useState(null).

  2. Type your data. Use TypeScript or JSDoc to declare that a variable is an array:

    /** @type {Array<{id: number, name: string}>} */
    const users = [];
  3. Validate API responses. Never assume an API returns the structure you expect. Always check:

    if (Array.isArray(response.data)) {
      setUsers(response.data);
    }
  4. Write tests for edge cases. Test what happens when an API returns null, an empty array, a single object, or a wrapped array.

  5. Use error tracking in production. Even with all this care, unexpected data shapes slip through. Understanding how to read a stack trace and correlating errors with error tracking best practices helps you catch and fix data mismatches fast.

The "map is not a function" error is a symptom of a data shape mismatch — usually preventable with careful initialization and validation. When errors do reach production, a proper error tracker captures the actual value and context, turning a cryptic error message into a solvable problem.

Start tracking errors in minutes

Start tracking these data-shape errors in production with LightTrace. Try free today — 5,000 events per month, no credit card required.

Fix your next production error faster

Point any Sentry SDK at LightTrace — free up to 5,000 events/month.