TypeError: X.map is not a function

⚡ Fastest fix

map() only exists on real arrays. The value you called it on is an object, null, or a single item — almost always because an API response envelope wraps the array ({ data: [...] }). Log the value, unwrap the real array, and guard with Array.isArray:

const list = Array.isArray(response.data) ? response.data : [];
list.map(item => item.id);   // ✅ never throws, even if the shape changes

What you're seeing

const response = await fetch("/api/users").then(r => r.json());
response.map(u => u.name);
// Uncaught TypeError: response.map is not a function
//   -> response is { data: [...], page: 1, total: 50 }, not a bare array

30-second triage

Step 0 — log the real shape

console.log(response, Array.isArray(response), typeof response);

Or paste a captured response into the JSON Formatter to see its structure at a glance before writing the fix.

Fix 1 — unwrap a paginated/envelope response (most common)

When: the API wraps the list in an object with metadata (page, total, etc.).

const response = await fetch("/api/users").then(r => r.json());
// response = { data: [...], page: 1, total: 50 }

response.map(u => u.name);        // ❌ response itself has no .map
response.data.map(u => u.name);   // ✅ the array is nested under "data"

Fix 2 — normalize an inconsistent single-vs-list response

When: the endpoint returns a bare object for one match, an array for many.

const raw = await fetchUsers(query);
// raw is a single {id, name} object when there's exactly one match,
// an array of them otherwise — an inconsistent API design, but a real one.

const users = Array.isArray(raw) ? raw : [raw];   // ✅ always an array from here on
users.map(u => u.name);

Fix 3 — guard with Array.isArray, not a truthy or length check

When: the value can be null, an empty string, or missing entirely when there's nothing to show.

function safeMap(value, fn) {
  return Array.isArray(value) ? value.map(fn) : [];
}

safeMap(response.data, u => u.name);   // ✅ [] instead of throwing, whatever the shape

// why not just check value.length?
"hello".length;   // 5 — a string LOOKS array-like by length, but has no .map
Array.isArray("hello");   // false — the correct, specific check

Why this happens

map() is defined on Array.prototype, so it only exists on actual arrays (or array-likes that explicitly borrow it). When code assumes a value is an array based on habit or documentation rather than checking, any deviation — a wrapping envelope object, a single-result shortcut, a null for "no results" — throws the moment .map is looked up and found missing. This is a shape mismatch, not a bug in map() itself; the fix is always to make the value actually be an array before calling it, or to explicitly not call .map() when it isn't.

Common variants at a glance

Shape you actually gotWhy .map is missingFix
{ data: [...], page, total }array is nested, not the top levelindex into the real field (Fix 1)
single object, not an arrayAPI "simplifies" a one-result casenormalize with Array.isArray (Fix 2)
null / empty stringAPI represents "no results" this waydefault to [] (Fix 3)
a Set or Map objectnot an Array — use [...set].map()spread into an array first

✓ Confirm it's fixed

  • Call the same code path with an empty result, a single result, and a multi-item result — none should throw.
  • Array.isArray(value) guards every .map()/.filter()/.forEach() call on API-derived data.
  • TypeScript, if you use it, should type the field as the real array type so this is a compile-time error next time, not a runtime one.

Frequently Asked Questions

What does 'X.map is not a function' mean?

map() is an Array.prototype method — it only exists on real arrays. If X is an object, a string, null, undefined, or a single item instead of a list, it has no map method and calling X.map(...) throws this TypeError. The fix is always about the shape of X, not about map() itself.

Why does my API response fail this way?

Many APIs wrap the array in an envelope object — { data: [...], page: 1, total: 50 } — so the array is response.data, not response itself. Calling response.map(...) tries to call map on the envelope object, which has no such method. Log the raw response shape before assuming it's a bare array.

Why does it only fail sometimes?

A common pattern: an endpoint returns an array for a list of results but a single object when there's exactly one match, or null/empty string when there are none — some backends do this to "simplify" the response, but it makes the shape inconsistent. Normalize the result to always be an array before mapping over it.

How do I guard against this safely?

Array.isArray(value) is the reliable check — it works even if value is null, a string, or a plain object, none of which would be caught by a truthy check alone. Use it to branch or to normalize: const list = Array.isArray(value) ? value : [];

Is checking value.length enough instead of Array.isArray?

No — strings also have a .length property, so a string would pass a naive length check but still throw on .map(). Array.isArray is the only check that's specific to actual arrays; use it instead of duck-typing on length or a truthy check.

How do I fix 'X.map is not a function' fast?

Log the value and its type right before the .map() call to see what you actually received. If it's wrapped in an envelope, unwrap the correct field. If it's sometimes a single object, normalize it to an array. Then guard permanently with Array.isArray so a future shape change fails safely instead of crashing.

Is the data shaped how you think?

Paste the API response into the formatter to see exactly which fields hold the array and how it's nested — nothing is uploaded to a server.

JSON Formatter All Error References HTTP Status Codes
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.