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 aView on GitHub (pinned to 8b06340921)
Solutions
- Audit source/transformer plugins for runaway node generation: log node count per plugin and cap loops/pagination.
- Ensure createNode is not being called inside a recursive/unbounded loop or re-emitting the same nodes with new IDs each build.
- If legitimately creating a huge node volume, deduplicate by stable content digest and reuse existing node IDs instead of minting new ones.
- 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
- Always derive node ids from stable content digests so duplicate sources do not mint new nodes.
- Log per-plugin node counts in onPluginLifecycle and set a soft ceiling alert.
- Never call createNode inside an unbounded while/for loop without a known termination bound.
- Periodically run gatsby clean if you suspect the persisted counter has drifted.
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
- Plugins creating nodes can not set data on the reserved fiel
- An error occurred finding a node by it's type. This is likel
- Could not find owner plugin of node for loadNodeContent with
- Could not find function loadNodeContent for plugin ${plugin.
- The node internal.owner field is set automatically by Gatsby
AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13).
Data as JSON: /api/errors/4dca0c508580445f.
Report an issue: GitHub.