{"record":{"id":"10f838e4ab3e6cf5","repo":"gatsbyjs/gatsby","slug":"oops-the-plugin-option-defaultdatalayer-should-b","errorCode":null,"errorMessage":"Oops the plugin option \"defaultDataLayer\" should be a plain object. \"${dataLayer}\" is not valid.","messagePattern":"Oops the plugin option \"defaultDataLayer\" should be a plain object\\. \"(.+?)\" is not valid\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/gatsby-plugin-google-tagmanager/src/gatsby-ssr.js","lineNumber":27,"sourceCode":"  selfHostedPath,\n}) => stripIndent`\n  (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':\n  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],\n  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=\n  '${selfHostedOrigin}/${selfHostedPath}?id='+i+dl+'${environmentParamStr}';f.parentNode.insertBefore(j,f);\n  })(window,document,'script','${dataLayerName}', '${id}');`\n\nconst generateGTMIframe = ({ id, environmentParamStr, selfHostedOrigin }) =>\n  oneLine`<iframe src=\"${selfHostedOrigin}/ns.html?id=${id}${environmentParamStr}\" height=\"0\" width=\"0\" style=\"display: none; visibility: hidden\" aria-hidden=\"true\"></iframe>`\n\nconst generateDefaultDataLayer = (dataLayer, reporter, dataLayerName) => {\n  let result = `window.${dataLayerName} = window.${dataLayerName} || [];`\n\n  if (dataLayer.type === `function`) {\n    result += `window.${dataLayerName}.push((${dataLayer.value})());`\n  } else {\n    if (dataLayer.type !== `object` || dataLayer.value.constructor !== Object) {\n      reporter.panic(\n        `Oops the plugin option \"defaultDataLayer\" should be a plain object. \"${dataLayer}\" is not valid.`\n      )\n    }\n\n    result += `window.${dataLayerName}.push(${JSON.stringify(\n      dataLayer.value\n    )});`\n  }\n\n  return stripIndent`${result}`\n}\n\nexports.onRenderBody = (\n  { setHeadComponents, setPreBodyComponents, reporter },\n  {\n    id,\n    includeInDevelopment = false,\n    gtmAuth,","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/gatsbyjs/gatsby/blob/8b06340921ffdf23125a365b9c9923690cb62ce6/packages/gatsby-plugin-google-tagmanager/src/gatsby-ssr.js#L9-L45","documentation":"The gatsby-plugin-google-tagmanager plugin validates the 'defaultDataLayer' option inside generateDefaultDataLayer (gatsby-ssr.js:26). It accepts either a function (dataLayer.type === 'function') or a plain object. A plain object is defined as dataLayer.type === 'object' AND dataLayer.value.constructor === Object — meaning the value must be a literal {} (Object constructor), not an array, Map, class instance, Date, or any subclass. If neither condition holds, reporter.panic halts the build. The dataLayer is Gatsby's pre-processed representation where .type is the JS typeof result and .value is the raw user-supplied value.","triggerScenarios":"Passing an array (e.g. defaultDataLayer: [{ page: 'home' }]) instead of a plain object. Passing a class instance, Map, Set, Date, or any non-literal object whose constructor is not the global Object. Passing a string or number that Gatsby did not pre-classify as a function type.","commonSituations":"Copying a dataLayer array from GTM documentation (GTM expects an array at runtime, but the plugin option wants an object). Migrating from a manual GTM snippet where dataLayer was an array. Passing a JSON-parsed value that is actually an array.","solutions":["Change defaultDataLayer to a single plain object literal: { platform: 'gatsby', pageType: 'home' }","If you need multiple data layer pushes, use a function: defaultDataLayer: function() { return { ... } }","Remove the option entirely if you don't need a default data layer push."],"exampleFix":"// before (gatsby-config.js)\n{\n  resolve: `gatsby-plugin-google-tagmanager`,\n  options: {\n    id: `GTM-XXXX`,\n    defaultDataLayer: [{ platform: `gatsby` }], // array — triggers panic\n  },\n},\n\n// after\n{\n  resolve: `gatsby-plugin-google-tagmanager`,\n  options: {\n    id: `GTM-XXXX`,\n    defaultDataLayer: { platform: `gatsby` }, // plain object\n  },\n},","handlingStrategy":"type-guard","validationCode":"// Before passing defaultDataLayer to gatsby-config, validate it\nfunction isPlainObject(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) && v.constructor === Object\n}\n\nconst defaultDataLayer = { platform: 'gatsby' }\nif (!isPlainObject(defaultDataLayer) && typeof defaultDataLayer !== 'function') {\n  throw new Error('defaultDataLayer must be a plain object or function')\n}","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' &&\n    v !== null &&\n    !Array.isArray(v) &&\n    v.constructor === Object\n}\n\n// Usage:\nconst dataLayer = pluginOptions.defaultDataLayer\nif (typeof dataLayer === 'function') {\n  // valid: function type\n} else if (!isPlainObject(dataLayer)) {\n  throw new Error('defaultDataLayer must be a plain object')\n}","tryCatchPattern":null,"preventionTips":["Always use a literal object {} for defaultDataLayer, never an array","Remember GTM's runtime dataLayer is an array, but the plugin option expects an object","Run a type check on plugin options before starting the build"],"tags":["configuration","gatsby-plugin","validation","google-tagmanager"],"backgroundTag":null,"analyzedSha":"8b06340921ffdf23125a365b9c9923690cb62ce6","analyzedAt":"2026-08-13T02:36:21.405Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}