gatsbyjs/gatsby · critical
Panicking because nodes appear to be being changed every tim
Error message
Panicking because nodes appear to be being changed every time we run queries. This would cause the site to recompile infinitely.\n Check custom resolvers to see if they are unconditionally creating or mutating nodes on every query.\n This may happen if they create nodes with a field that is different every time, such as a timestamp or unique id.
What it means
Gatsby detects an infinite loop: nodes are being mutated or created on every query run cycle, causing the query extraction and compilation to re-trigger endlessly. The check fires when the develop state machine observes node changes repeatedly across query runs. Custom resolvers that unconditionally generate non-deterministic data (timestamps, random IDs) are the typical cause.
Source
Thrown at packages/gatsby/src/state-machines/develop/actions.ts:176
export const logError: ActionFunction<IBuildContext, AnyEventObject> = (
_context,
event
) => {
reporter.error(event.data)
}
export const panic: ActionFunction<IBuildContext, AnyEventObject> = (
_context,
event
) => {
reporter.panic(event.data)
}
export const panicBecauseOfInfiniteLoop: ActionFunction<
IBuildContext,
AnyEventObject
> = () => {
reporter.panic(
reporter.stripIndent(`
Panicking because nodes appear to be being changed every time we run queries. This would cause the site to recompile infinitely.
Check custom resolvers to see if they are unconditionally creating or mutating nodes on every query.
This may happen if they create nodes with a field that is different every time, such as a timestamp or unique id.`)
)
}
export const trackRequestedQueryRun = assign<IBuildContext, AnyEventObject>({
pendingQueryRuns: (context, { payload }) => {
const pendingQueryRuns = context.pendingQueryRuns || new Set<string>()
if (payload?.pagePath) {
pendingQueryRuns.add(payload.pagePath)
}
return pendingQueryRuns
},
})
export const clearPendingQueryRuns = assign<IBuildContext>(() => {View on GitHub (pinned to 8b06340921)
Solutions
- Audit all custom resolvers and onCreateNode/sourceNodes handlers for non-deterministic fields (timestamps, random values, unseeded UUIDs).
- Remove or stabilize the offending field -- use a fixed value, a deterministic hash, or only set it once per node creation.
- Always use createNodeId(id) for stable node IDs instead of uuid().
- Ensure source plugins only create nodes when data actually changes (diff before writing).
Example fix
// before -- non-deterministic field causes infinite loop
exports.createResolvers = ({ createResolvers }) => {
createResolvers({
MyType: {
lastChecked: {
type: 'String',
resolve: () => new Date().toISOString(), // changes every time!
},
},
})
}
// after -- deterministic value
exports.createResolvers = ({ createResolvers }) => {
createResolvers({
MyType: {
lastChecked: {
type: 'String',
resolve: (source) => source.updatedAt || '1970-01-01',
},
},
})
} Defensive patterns
Strategy: validation
Validate before calling
// Detect non-deterministic fields in resolvers before deploying
function auditResolversForNonDeterminism(resolverCode) {
const redFlags = [
/new Date\(\)/,
/Date\.now\(\)/,
/Math\.random\(\)/,
/uuid\(\)/,
/crypto\.randomBytes/,
]
return redFlags.filter(function(re) { return re.test(resolverCode) }).map(function(re) { return re.source })
}
var flags = auditResolversForNonDeterminism(myResolverSource)
if (flags.length) console.warn('Non-deterministic calls found:', flags) Prevention
- Never use new Date(), Date.now(), Math.random(), or unseeded uuid() in resolvers or node-creation hooks.
- Always use createNodeId(id) for stable, deterministic node IDs.
- In source plugins, diff data and only call createNode when content actually changed.
- Audit custom resolvers for any field whose value differs across invocations.
When it happens
Trigger: A createResolvers custom resolver, a createNode call inside a query lifecycle, or a source plugin that re-creates nodes with a field that changes every invocation (e.g. new Date(), Math.random(), uuid() without seeding). Each query run sees 'new' node data, triggering another query run.
Common situations: A custom resolver adds a `lastUpdated: new Date().toISOString()` field. A source plugin regenerates node IDs non-deterministically on each run. A onCreateNode handler that calls createNode with a timestamp field. Using uuid() instead of createNodeId for stable IDs.
Related errors
- Could not create more nodes. Maximum node count is reached:
- Plugins creating nodes can not set data on the reserved fiel
- Could not find owner plugin of node for loadNodeContent with
- Could not find function loadNodeContent for plugin ${plugin.
- for custom ssl --https, --cert-file, and --key-file must be
AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13).
Data as JSON: /api/errors/8e772f4aad5750f4.
Report an issue: GitHub.