{"record":{"id":"8e772f4aad5750f4","repo":"gatsbyjs/gatsby","slug":"panicking-because-nodes-appear-to-be-being-changed","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/gatsby/src/state-machines/develop/actions.ts","lineNumber":176,"sourceCode":"export const logError: ActionFunction<IBuildContext, AnyEventObject> = (\n  _context,\n  event\n) => {\n  reporter.error(event.data)\n}\n\nexport const panic: ActionFunction<IBuildContext, AnyEventObject> = (\n  _context,\n  event\n) => {\n  reporter.panic(event.data)\n}\n\nexport const panicBecauseOfInfiniteLoop: ActionFunction<\n  IBuildContext,\n  AnyEventObject\n> = () => {\n  reporter.panic(\n    reporter.stripIndent(`\n  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.`)\n  )\n}\n\nexport const trackRequestedQueryRun = assign<IBuildContext, AnyEventObject>({\n  pendingQueryRuns: (context, { payload }) => {\n    const pendingQueryRuns = context.pendingQueryRuns || new Set<string>()\n    if (payload?.pagePath) {\n      pendingQueryRuns.add(payload.pagePath)\n    }\n    return pendingQueryRuns\n  },\n})\n\nexport const clearPendingQueryRuns = assign<IBuildContext>(() => {","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/gatsbyjs/gatsby/blob/8b06340921ffdf23125a365b9c9923690cb62ce6/packages/gatsby/src/state-machines/develop/actions.ts#L158-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before -- non-deterministic field causes infinite loop\nexports.createResolvers = ({ createResolvers }) => {\n  createResolvers({\n    MyType: {\n      lastChecked: {\n        type: 'String',\n        resolve: () => new Date().toISOString(), // changes every time!\n      },\n    },\n  })\n}\n\n// after -- deterministic value\nexports.createResolvers = ({ createResolvers }) => {\n  createResolvers({\n    MyType: {\n      lastChecked: {\n        type: 'String',\n        resolve: (source) => source.updatedAt || '1970-01-01',\n      },\n    },\n  })\n}","handlingStrategy":"validation","validationCode":"// Detect non-deterministic fields in resolvers before deploying\nfunction auditResolversForNonDeterminism(resolverCode) {\n  const redFlags = [\n    /new Date\\(\\)/,\n    /Date\\.now\\(\\)/,\n    /Math\\.random\\(\\)/,\n    /uuid\\(\\)/,\n    /crypto\\.randomBytes/,\n  ]\n  return redFlags.filter(function(re) { return re.test(resolverCode) }).map(function(re) { return re.source })\n}\n\nvar flags = auditResolversForNonDeterminism(myResolverSource)\nif (flags.length) console.warn('Non-deterministic calls found:', flags)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["infinite-loop","resolvers","nodes","develop","non-deterministic"],"backgroundTag":null,"analyzedSha":"8b06340921ffdf23125a365b9c9923690cb62ce6","analyzedAt":"2026-08-13T02:36:21.405Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}