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
- Add a 'exports <pkg> to <targetName>' or 'opens <pkg> to <targetName>' directive in the source module's module-info.java.
- Verify the Module passed to addExportsAndOpens is the intended named module (check target.getName() against the descriptor).
- 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
- Keep module-info.java qualified exports/opens in sync with every target module you wire at runtime.
- Add a build-time test that asserts getTargets() contains each module you intend to wire.
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
- Error trying to read [{}#ARGUMENTS]
- module-info.class no found in ${path}
- expected modules ${listToString(EXPECTED_ES_SERVER_MODULES)}
- Could not create output directory: {parent}
- automatic module without a manifest name is not supported, f
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/a1665421a8f14766.
Report an issue: GitHub.