gatsbyjs/gatsby · critical · Error

Could not create more nodes. Maximum node count is reached:

Error message

Could not create more nodes. Maximum node count is reached: ${lastNodeCounter}

What it means

Gatsby assigns each node a monotonically increasing integer counter (stored in redux state.status.LAST_NODE_COUNTER) for fast comparisons/intersections of node slices. getNextNodeCounter() throws when this counter has already reached Number.MAX_SAFE_INTEGER (2^53 - 1), since no further unique counter value can be safely represented. This is a hard internal ceiling on the total number of nodes ever created, even across builds (the counter persists). Hitting it implies an astronomically large node volume or a runaway generator that never stops emitting nodes.

Source

Thrown at packages/gatsby/src/redux/actions/public.js:588

  // It's possible the file node was never created as sometimes tools will
  // write and then immediately delete temporary files to the file system.
  const deleteDescendantsActions =
    internalNode &&
    findChildren(internalNode.children).map(getNode).map(createDeleteAction)

  if (deleteDescendantsActions && deleteDescendantsActions.length) {
    return [...deleteDescendantsActions, deleteAction]
  } else {
    return deleteAction
  }
}

// We add a counter to node.internal for fast comparisons/intersections
// of various node slices. The counter must increase even across builds.
function getNextNodeCounter() {
  const lastNodeCounter = store.getState().status.LAST_NODE_COUNTER ?? 0
  if (lastNodeCounter >= Number.MAX_SAFE_INTEGER) {
    throw new Error(
      `Could not create more nodes. Maximum node count is reached: ${lastNodeCounter}`
    )
  }
  return lastNodeCounter + 1
}

// memberof notation is added so this code can be referenced instead of the wrapper.
/**
 * Create a new node.
 * @memberof actions
 * @param {Object} node a node object
 * @param {string} node.id The node's ID. Must be globally unique.
 * @param {string} node.parent The ID of the parent's node. If the node is
 * derived from another node, set that node as the parent. Otherwise it can
 * just be `null`.
 * @param {Array} node.children An array of children node IDs. If you're
 * creating the children nodes while creating the parent node, add the
 * children node IDs here directly. If you're adding a child node to a

View on GitHub (pinned to 8b06340921)

Solutions

  1. Audit source/transformer plugins for runaway node generation: log node count per plugin and cap loops/pagination.
  2. Ensure createNode is not being called inside a recursive/unbounded loop or re-emitting the same nodes with new IDs each build.
  3. If legitimately creating a huge node volume, deduplicate by stable content digest and reuse existing node IDs instead of minting new ones.
  4. As a last resort, clear persisted redux state (.cache/redux state, LAST_NODE_COUNTER) and rebuild from a clean source.

Example fix

// before
for (const item of items) {
  for (const variant of item.variants) {
    createNode({ id: `${item.id}-${variant.id}-${Date.now()}`, ... }) // unbounded, ever-growing
  }
}
// after - stable IDs, deduped, no unbounded growth
for (const item of items) {
  createNode({ id: `item-${item.id}`, parent: null, ... })
}
Defensive patterns

Strategy: validation

Validate before calling

// Before bulk-creating nodes, estimate count and cap generation.
const MAX = Number.MAX_SAFE_INTEGER // informational only
const planned = items.reduce((n, i) => n + i.variants.length, 0)
if (planned > 5_000_000) {
  throw new Error(`Refusing to generate ${planned} nodes; possible runaway source`)
}
// Prefer stable IDs + content-digest dedupe so re-runs do not grow the counter.

Type guard

// Guard against passing an object whose generation would be unbounded.
function isBoundedNodeSource(items) {
  return Array.isArray(items) && items.length > 0 && items.every(i =>
    i && typeof i.id === 'string' && Array.isArray(i.variants)
  )
}

Prevention

When it happens

Trigger: Invoked implicitly inside createNode()/createNodeField paths every time a node is created and sanitizeNode assigns it an internal.counter. The throw fires only when store.getState().status.LAST_NODE_COUNTER >= 9007199254740991 at the moment of increment.

Common situations: A source plugin stuck in an infinite loop creating nodes (e.g. pagination/paging bug generating millions of duplicate nodes), or importing a massive dataset whose items each fan out into many child nodes across repeated builds (counter never resets, even after DELETE_CACHE only resets some state). Practically unreachable except via buggy generation logic.

Related errors


AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13). Data as JSON: /api/errors/4dca0c508580445f. Report an issue: GitHub.