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

  1. Make the target class's intended constructor public (and the class itself public).
  2. 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.
  3. If the class is in your JPMS module, add `opens my.dtos;` to module-info.java.
  4. 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

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


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/cd6341c1421fe307. Report an issue: GitHub.