{"record":{"id":"cd6341c1421fe307","repo":"apple/pkl","slug":"error-accessing-constructor-s","errorCode":null,"errorMessage":"Error accessing constructor `%s`.","messagePattern":"Error accessing constructor `(.+?)`\\.","errorType":"exception","errorClass":"ConversionException","httpStatus":null,"severity":"error","filePath":"pkl-config-java/src/main/java/org/pkl/config/java/mapper/PObjectToDataObject.java","lineNumber":66,"sourceCode":"  protected PObjectToDataObject() {}\n\n  @Override\n  public final Optional<Converter<?, ?>> create(PClassInfo<?> sourceType, Type targetType) {\n    if (!(sourceType == PClassInfo.Module || sourceType.getJavaClass() == PObject.class)) {\n      return Optional.empty();\n    }\n\n    return selectConstructor(Reflection.toRawType(targetType))\n        .flatMap(\n            constructor ->\n                getParameters(constructor, targetType)\n                    .map(\n                        parameters -> {\n                          try {\n                            return new ConverterImpl<>(\n                                targetType, lookup.unreflectConstructor(constructor), parameters);\n                          } catch (IllegalAccessException e) {\n                            throw new ConversionException(\n                                String.format(\"Error accessing constructor `%s`.\", constructor), e);\n                          }\n                        }));\n  }\n\n  protected Optional<Constructor<?>> selectConstructor(Class<?> clazz) {\n    return Arrays.stream(clazz.getDeclaredConstructors())\n        .max(Comparator.comparingInt(Constructor::getParameterCount));\n  }\n\n  protected Optional<List<String>> getParameterNames(Constructor<?> constructor) {\n    var paramNames = new ArrayList<String>(constructor.getParameterCount());\n\n    var properties = getAnnotation(constructor, ConstructorProperties.class);\n    if (properties != null) {\n      return Optional.of(Arrays.asList(properties.value()));\n    }\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-config-java/src/main/java/org/pkl/config/java/mapper/PObjectToDataObject.java#L48-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nclass Data {\n  Data(String name, int age) {} // package-private, widest ctor -> selected, then inaccessible\n}\n// after\npublic class Data {\n  public Data(String name, int age) {}\n}\n// or module-info.java: opens my.dtos;\n","handlingStrategy":"validation","validationCode":"// Ensure the widest constructor of the target data class is public before conversion:\nstatic boolean widestCtorIsPublic(Class<?> c) {\n  return java.util.Arrays.stream(c.getDeclaredConstructors())\n      .max(java.util.Comparator.comparingInt(Constructor::getParameterCount))\n      .map(ctor -> java.lang.reflect.Modifier.isPublic(ctor.getModifiers()))\n      .orElse(false);\n}","typeGuard":"static boolean isRenderableDataObject(Class<?> c) {\n  return java.lang.reflect.Modifier.isPublic(c.getModifiers())\n      && java.util.Arrays.stream(c.getDeclaredConstructors())\n          .anyMatch(ctor -> java.lang.reflect.Modifier.isPublic(ctor.getModifiers()));\n}","tryCatchPattern":"try {\n  MyData data = valueRenderer.render(pObject, MyData.class);\n} catch (ConversionException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Error accessing constructor\")) {\n    throw new IllegalStateException(\"Make MyData's constructor public or open its package in module-info.java\", e);\n  } else throw e;\n}","preventionTips":["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."],"tags":["java","reflection","access-denied","jpms","data-object-mapping"],"backgroundTag":"permission-denied","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}