{"record":{"id":"69b722a8bd880dba","repo":"gatsbyjs/gatsby","slug":"gatsby-can-t-differentiate-between-themes-matchi","errorCode":null,"errorMessage":"Gatsby can't differentiate between themes ${matchingThemes.map(theme => theme.themeName).join(` and `)} for path ${filepath}","messagePattern":"Gatsby can't differentiate between themes (.+?) for path (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/gatsby/src/internal-plugins/webpack-theme-component-shadowing/index.js","lineNumber":253,"sourceCode":"    }\n    return null\n  }\n\n  getThemeAndComponent(filepath) {\n    // find out which theme's src/components dir we're requiring from\n    const allMatchingThemes = this.themes.filter(({ themeDir }) =>\n      filepath.startsWith(path.join(themeDir, `src`))\n    )\n\n    // The same theme can be included twice in the themes list causing multiple\n    // matches. This case should only be counted as a single match for that theme.\n    const matchingThemes = _.uniqBy(allMatchingThemes, `themeName`)\n\n    // 0 matching themes happens a lot for paths we don't want to handle\n    // > 1 matching theme means we have a path like\n    //   `gatsby-theme-blog/src/components/gatsby-theme-something/src/components`\n    if (matchingThemes.length > 1) {\n      throw new Error(\n        `Gatsby can't differentiate between themes ${matchingThemes\n          .map(theme => theme.themeName)\n          .join(` and `)} for path ${filepath}`\n      )\n    }\n\n    if (matchingThemes.length === 0) {\n      return [null, null]\n    }\n\n    const theme = matchingThemes[0]\n\n    // get the location of the component relative to its theme's src/\n    const [, component] = filepath.split(path.join(theme.themeDir, `src`))\n\n    return [theme, component]\n  }\n","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/gatsbyjs/gatsby/blob/8b06340921ffdf23125a365b9c9923690cb62ce6/packages/gatsby/src/internal-plugins/webpack-theme-component-shadowing/index.js#L235-L271","documentation":"Theme component shadowing matches a file path against each theme's `src/` directory. After de-duplicating, if two distinct themes both claim the path (their `src` prefixes both match), Gatsby cannot decide which theme owns the shadowed component and aborts.","triggerScenarios":"A path such as `gatsby-theme-blog/src/components/gatsby-theme-something/src/...` where one theme's directory prefix is nested inside another theme's prefix, yielding two unique matches.","commonSituations":"A theme that itself depends on another theme and re-exports components under a path that starts with the outer theme's `src`; renamed/duplicated theme packages sharing a `themeDir` prefix.","solutions":["Rename the inner theme's package/`themeDir` so its `src` path is not a prefix collision with the outer theme.","Move the shadowed component out of the colliding path.","Avoid having one theme shadow another theme inside a directory whose name equals the outer theme's `src` tree."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// For theme authors: assert no two theme dirs share a prefix collision.\nfunction assertNoShadowCollision(themes) {\n  const dirs = themes.map(t => path.join(t.themeDir, \"src\"))\n  for (let i = 0; i < dirs.length; i++) {\n  for (let j = i + 1; j < dirs.length; j++) {\n  if (dirs[i].startsWith(dirs[j]) || dirs[j].startsWith(dirs[i])) {\n  throw new Error(`Theme dirs collide: ${dirs[i]} <-> ${dirs[j]}`)\n  }\n  }\n  }\n}\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Give each theme a unique package name and `themeDir` so `src` prefixes never nest.","Avoid shadowing one theme's component from inside another theme's `src` tree.","Test shadowing in a fixture site that mounts both themes together."],"tags":["themes","shadowing","webpack","gatsby","config"],"backgroundTag":null,"analyzedSha":"8b06340921ffdf23125a365b9c9923690cb62ce6","analyzedAt":"2026-08-13T02:36:21.405Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}