JetBrains/intellij-community · error · ConfigurationException

module.paths.validation.duplicate.source.root.error

module.paths.validation.duplicate.source.root.error

Error message

Module ''{0}''
must not contain source root ''{1}''.
The root already belongs to module ''{2}''

What it means

Thrown by ModulesConfigurator.throwDuplicateSourceRootError when a source root is claimed by a module whose content root is nested inside another module's content root that already owns that source root. The configurator determines the 'problematic' module (the one with the nested content root, via VfsUtilCore.isAncestor) and the 'correct' module, and rejects the model until the nested module stops claiming the source root.

Source

Thrown at java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java:339

  /**
   * Throws an appropriate error for duplicate source roots.
   */
  private static void throwDuplicateSourceRootError(@NotNull VirtualFile srcRoot,
                                                    @NotNull VirtualFile contentRoot,
                                                    @NotNull VirtualFile anotherContentRoot,
                                                    @NotNull Map<VirtualFile, String> contentRootToModuleNameMap) throws ConfigurationException {
    final String problematicModule;
    final String correctModule;
    if (VfsUtilCore.isAncestor(anotherContentRoot, contentRoot, true)) {
      problematicModule = contentRootToModuleNameMap.get(anotherContentRoot);
      correctModule = contentRootToModuleNameMap.get(contentRoot);
    }
    else {
      problematicModule = contentRootToModuleNameMap.get(contentRoot);
      correctModule = contentRootToModuleNameMap.get(anotherContentRoot);
    }
    throw new ConfigurationException(
      JavaUiBundle.message("module.paths.validation.duplicate.source.root.error", problematicModule, srcRoot.getPresentableUrl(), correctModule)
    );
  }

  /**
   * Validates that source roots belong to the same module as their corresponding content root.
   */
  private static void validateSourceRootsAcrossModules(@NotNull Map<VirtualFile, VirtualFile> srcRootsToContentRootMap,
                                                       @NotNull Map<VirtualFile, String> contentRootToModuleNameMap) throws ConfigurationException {
    for (Map.Entry<VirtualFile, VirtualFile> entry : srcRootsToContentRootMap.entrySet()) {
      final VirtualFile srcRoot = entry.getKey();
      final VirtualFile correspondingContent = entry.getValue();
      final String expectedModuleName = contentRootToModuleNameMap.get(correspondingContent);

      for (VirtualFile candidateContent = srcRoot;
           candidateContent != null && !candidateContent.equals(correspondingContent);
           candidateContent = candidateContent.getParent()) {
        final String moduleName = contentRootToModuleNameMap.get(candidateContent);

View on GitHub (pinned to be881553f2)

Solutions

  1. Remove the source root from the 'problematic' module named in the message (Project Structure > Modules > that module > unmark/remove the folder)
  2. Or shrink the parent module's source roots so they no longer cover the child module's directory
  3. Re-import from the build tool (Gradle/Maven refresh) so content root ownership is derived correctly instead of hand-edited

Example fix

<!-- before: parent module owns p/src/main/java; child module also claims it -->
<!-- parent.iml -->
<content url="file://$PROJECT_DIR$/p">
  <sourceFolder url="file://$PROJECT_DIR$/p/src/main/java"/>
</content>
<!-- child.iml -->
<content url="file://$PROJECT_DIR$/p/src/main/java">
  <sourceFolder url="file://$PROJECT_DIR$/p/src/main/java"/>
</content>

<!-- after: child keeps the dir as content root, parent stops marking it as source -->
<!-- parent.iml -->
<content url="file://$PROJECT_DIR$/p"/>
<!-- child.iml -->
<content url="file://$PROJECT_DIR$/p/src/main/java">
  <sourceFolder url="file://$PROJECT_DIR$/p/src/main/java"/>
</content>
Defensive patterns

Strategy: validation

Validate before calling

Map<VirtualFile, String> srcOwner = new HashMap<>();
for (Module m : modules) {
  for (ContentEntry c : ModuleRootManager.getInstance(m).getContentEntries()) {
    for (VirtualFile src : c.getSourceFolderFiles()) {
      String prev = srcOwner.put(src, m.getName());
      if (prev != null && !prev.equals(m.getName())) {
        // nested overlap: unmark src from the nested module or shrink the parent's roots
      }
    }
  }
}

Try / catch

try {
  configurator.apply();
} catch (ConfigurationException e) {
  // message identifies problematic module and correct module; unmark the source root
  // from the problematic module, then re-apply
}

Prevention

When it happens

Trigger: During apply-time validation, srcRootsToContentRootMap.put(srcRoot, contentRoot) finds the source root already mapped to a different content root; the classic shape is module A with content root 'p' + source root 'p/src/main/java', and module B with content root 'p/src/main/java' also claiming 'p/src/main/java' as a source root.

Common situations: Maven/Gradle import where a submodule directory sits inside the parent's source tree and both get content roots; hand-edited iml giving a submodule the parent's src as its own source root; importing nested builds (parent + child aggregator) twice with different roots.

Related errors


AI-assisted analysis of JetBrains/intellij-community@be881553f2 (2026-08-14). Data as JSON: /api/errors/28ad452364ee2fd7. Report an issue: GitHub.