# Empty collections return empty array

- **Pattern:** `ab-000375` (`api-design.developer-ergonomics.empty-collections`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000375
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000375)

## Why it matters

An endpoint that returns 404 when a collection is empty conflates two distinct states: "this endpoint does not exist" and "this collection has no items." Consumers must add special-case 404 handling to every list call, and any generic list component breaks for new users who have no data yet. Returning `null` instead of `[]` causes `response.data.map is not a function` in every consumer that did not add a null guard. iso-25010:2011 compatibility.interoperability requires a consistent response shape regardless of result count — consumers must be able to write one code path for all list responses.

## Severity rationale

Low because inconsistent empty collection handling causes consumer-side null reference errors and forces special-case code without enabling security exploits.

## Remediation

Always return an empty array with 200 status and consistent pagination metadata when a collection has no items:

```ts
// Before -- inconsistent empty handling:
if (results.length === 0) {
  return Response.json({ message: 'No items found' }, { status: 404 })
}

// After -- consistent shape regardless of count:
return Response.json({
  data: results,  // [] when empty, same shape as non-empty
  meta: { page: 1, limit: 20, total: 0, totalPages: 0 }
})
```

This lets consumers write `response.data.map(...)` once and have it work whether the array has 0 or 1000 items. The rule is simple: 404 means the endpoint does not exist, never that the collection is empty.

## Detection

- **ID:** `empty-collections`
- **Severity:** `low`
- **What to look for:** Check what list endpoints return when the collection is empty. The correct behavior is to return an empty array `[]` (with 200 status and pagination metadata showing 0 total). Look for anti-patterns: returning `null` instead of `[]`, returning 404 for empty collections (404 means the endpoint doesn't exist, not that the collection is empty), returning a different response shape for empty vs non-empty collections, or returning an error message like "No results found."
- **Pass criteria:** Count all list endpoints that return collections. All list endpoints return an empty array `[]` with a 200 status code when the collection has no items. The response shape is identical to a non-empty response (same metadata fields, same envelope). Pagination metadata correctly shows total: 0.
- **Fail criteria:** Empty collections return `null` instead of `[]`, return 404, return a different response shape than non-empty collections, or return an error/message instead of an empty data array. Should not pass when any list endpoint returns 404 for an empty collection.
- **Skip (N/A) when:** No list endpoints exist.
- **Detail on fail:** Identify the issue (e.g., "`GET /api/orders` returns 404 when user has no orders. `GET /api/comments` returns `{ data: null }` instead of `{ data: [] }`. `GET /api/notifications` returns `{ message: 'No notifications' }` instead of an empty array."). Max 500 chars.
- **Remediation:** Always return `[]` for empty collections with 200 status:

  ```ts
  // Before -- inconsistent empty handling:
  if (results.length === 0) {
    return Response.json({ message: 'No items found' }, { status: 404 })
  }

  // After -- consistent empty array:
  return Response.json({
    data: results,  // [] when empty, same shape as non-empty
    meta: { page: 1, limit: 20, total: 0, totalPages: 0 }
  })
  ```

  This lets consumers write one code path: `response.data.map(...)` works whether the array has 0 or 100 items.

---

## External references

- iso-25010 compatibility.interoperability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-000375
