apple/pkl · error · ConversionException
Error accessing constructor `%s`.
Error message
Error accessing constructor `%s`.
What it means
PObjectToDataObject converts Pkl PObject/Module values into Java data-object classes by reflectively invoking the target class's constructor (by default the one with the most parameters). `lookup.unreflectConstructor(constructor)` failed with IllegalAccessException, meaning the selected constructor is not accessible from the mapper's MethodHandles.Lookup context, so it is rethrown as this ConversionException naming the constructor.
Source
Thrown at pkl-config-java/src/main/java/org/pkl/config/java/mapper/PObjectToDataObject.java:66
protected PObjectToDataObject() {}
@Override
public final Optional<Converter<?, ?>> create(PClassInfo<?> sourceType, Type targetType) {
if (!(sourceType == PClassInfo.Module || sourceType.getJavaClass() == PObject.class)) {
return Optional.empty();
}
return selectConstructor(Reflection.toRawType(targetType))
.flatMap(
constructor ->
getParameters(constructor, targetType)
.map(
parameters -> {
try {
return new ConverterImpl<>(
targetType, lookup.unreflectConstructor(constructor), parameters);
} catch (IllegalAccessException e) {
throw new ConversionException(
String.format("Error accessing constructor `%s`.", constructor), e);
}
}));
}
protected Optional<Constructor<?>> selectConstructor(Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredConstructors())
.max(Comparator.comparingInt(Constructor::getParameterCount));
}
protected Optional<List<String>> getParameterNames(Constructor<?> constructor) {
var paramNames = new ArrayList<String>(constructor.getParameterCount());
var properties = getAnnotation(constructor, ConstructorProperties.class);
if (properties != null) {
return Optional.of(Arrays.asList(properties.value()));
}
View on GitHub (pinned to f3efcbfc9b)
Solutions
- Make the target class's intended constructor public (and the class itself public).
- Annotate the intended public constructor usage by reducing arity so `selectConstructor` (max parameter count) picks an accessible one, or override `selectConstructor` in a PObjectToDataObject subclass to select a public constructor.
- If the class is in your JPMS module, add `opens my.dtos;` to module-info.java.
- Map to a public DTO class with an accessible all-args constructor, optionally annotated with @ConstructorProperties for parameter names.
Example fix
// before
class Data {
Data(String name, int age) {} // package-private, widest ctor -> selected, then inaccessible
}
// after
public class Data {
public Data(String name, int age) {}
}
// or module-info.java: opens my.dtos;
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the widest constructor of the target data class is public before conversion:
static boolean widestCtorIsPublic(Class<?> c) {
return java.util.Arrays.stream(c.getDeclaredConstructors())
.max(java.util.Comparator.comparingInt(Constructor::getParameterCount))
.map(ctor -> java.lang.reflect.Modifier.isPublic(ctor.getModifiers()))
.orElse(false);
} Type guard
static boolean isRenderableDataObject(Class<?> c) {
return java.lang.reflect.Modifier.isPublic(c.getModifiers())
&& java.util.Arrays.stream(c.getDeclaredConstructors())
.anyMatch(ctor -> java.lang.reflect.Modifier.isPublic(ctor.getModifiers()));
} Try / catch
try {
MyData data = valueRenderer.render(pObject, MyData.class);
} catch (ConversionException e) {
if (e.getMessage() != null && e.getMessage().contains("Error accessing constructor")) {
throw new IllegalStateException("Make MyData's constructor public or open its package in module-info.java", e);
} else throw e;
} Prevention
- Give data-object classes a public constructor; remember selectConstructor picks the one with the most parameters, so make that one public.
- Annotate constructors with @ConstructorProperties (or compile with -parameters) so parameter names resolve and only accessible constructors need to work.
- Open DTO packages in module-info.java when using JPMS.
- Add a build-time check/test that every mapping-target class has an accessible max-arity constructor.
When it happens
Trigger: Rendering a Pkl object/module into a data class whose selected (max-arity) constructor is private, protected, or package-private, or whose class lives in a JPMS module/package not opened to the mapper, e.g. `valueRenderer.render(pObject, my.Data.class)` where Data's widest constructor is package-private.
Common situations: Data classes with hidden constructors enforcing factory-method creation; package-private DTOs in another package; Java 9+ modules that don't `opens` their packages; records/classes in third-party libraries with non-public constructors.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Error accessing constructor of class `%s`.
- Error accessing constructor of class `%s`.
- JavaType token must be parameterized.
- Error invoking constructor of class `%s`.
- Error invoking constructor of class `%s`.
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/cd6341c1421fe307.
Report an issue: GitHub.