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

  1. Audit all custom resolvers and onCreateNode/sourceNodes handlers for non-deterministic fields (timestamps, random values, unseeded UUIDs).
  2. Remove or stabilize the offending field -- use a fixed value, a deterministic hash, or only set it once per node creation.
  3. Always use createNodeId(id) for stable node IDs instead of uuid().
  4. 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

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


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