elastic/elasticsearch · error · IllegalArgumentException

Module {module.getName()} does not contain qualified exports

Error message

Module {module.getName()} does not contain qualified exports or opens for module {targetName}

What it means

Thrown by ModuleQualifiedExportsService.addExportsAndOpens when the target Module's name is not present in the precomputed 'targets' set of qualified-export recipients. The service is built from a module's descriptor listing which packages it qualified-exports/opens to specific named modules; attempting to wire exports to a module that was never declared as a qualified recipient is rejected. This guards the Java module-system reflection used to give ES internals access into JDK modules.

Source

Thrown at libs/core/src/main/java/org/elasticsearch/jdk/ModuleQualifiedExportsService.java:146

        targetsToSources.replaceAll((k, v) -> List.copyOf(v));
        return Map.copyOf(targetsToSources);
    }

    /**
     * Returns all qualified targets of the owning module.
     */
    public Set<String> getTargets() {
        return targets;
    }

    /**
     * Add exports and opens for a target module.
     * @param target A module whose name exists in {@link #getTargets()}
     */
    public void addExportsAndOpens(Module target) {
        String targetName = target.getName();
        if (targets.contains(targetName) == false) {
            throw new IllegalArgumentException(
                "Module " + module.getName() + " does not contain qualified exports or opens for module " + targetName
            );
        }
        List<String> exports = qualifiedExports.getOrDefault(targetName, List.of());
        for (String export : exports) {
            addExports(export, target);
        }
        List<String> opens = qualifiedOpens.getOrDefault(targetName, List.of());
        for (String open : opens) {
            addOpens(open, target);
        }
    }

    /**
     * Add a qualified export. This should always be implemented as follows:
     * <code>
     *     module.addExports(pkg, target);
     * </code>

View on GitHub (pinned to db6a809a66)

Solutions

  1. Add a 'exports <pkg> to <targetName>' or 'opens <pkg> to <targetName>' directive in the source module's module-info.java.
  2. Verify the Module passed to addExportsAndOpens is the intended named module (check target.getName() against the descriptor).
  3. Rebuild the module so the updated descriptor is picked up.

Example fix

// before: module-info.java has no qualified export to 'org.elasticsearch.foo'
module org.elasticsearch.core {
  exports org.elasticsearch.core;
}

// after
module org.elasticsearch.core {
  exports org.elasticsearch.core to org.elasticsearch.foo;
  opens org.elasticsearch.core to org.elasticsearch.foo;
}
Defensive patterns

Strategy: validation

Validate before calling

// Before calling addExportsAndOpens, verify the target is registered
ModuleQualifiedExportsService svc = ...;
Module target = ...;
if (!svc.getTargets().contains(target.getName())) {
  throw new IllegalStateException("module " + target.getName() + " is not a declared qualified target");
}
svc.addExportsAndOpens(target);

Prevention

When it happens

Trigger: Calling addExportsAndOpens(target) with a Module whose getName() is not in the targets set populated from the module descriptor's qualified exports and opens. The guard fires before any addExports/addOpens reflection call.

Common situations: Wiring a new target module without declaring a qualified export for it in module-info; renaming a module without updating the descriptor; passing the wrong Module instance (e.g. an unnamed or test module) to a service configured for a named one.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/a1665421a8f14766. Report an issue: GitHub.