{"record":{"id":"91b6c527d205c744","repo":"apple/pkl","slug":"annotation-fqn-is-not-a-valid-java-class-the-n","errorCode":null,"errorMessage":"Annotation `$fqn` is not a valid Java class.\nThe name of the annotation should be the canonical Java name of the class, for example, `com.example.Foo`.","messagePattern":"Annotation `\\$fqn` is not a valid Java class\\.\nThe name of the annotation should be the canonical Java name of the class, for example, `com\\.example\\.Foo`\\.","errorType":"exception","errorClass":"JavaCodeGeneratorException","httpStatus":null,"severity":"error","filePath":"pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/JavaCodeGenerator.kt","lineNumber":130,"sourceCode":"    private val DATA_SIZE = ClassName.get(DataSize::class.java)\n    private val DATASIZE_UNIT = ClassName.get(DataSizeUnit::class.java)\n    private val PAIR = ClassName.get(Pair::class.java)\n    private val COLLECTION = ClassName.get(Collection::class.java)\n    private val LIST = ClassName.get(List::class.java)\n    private val SET = ClassName.get(Set::class.java)\n    private val MAP = ClassName.get(Map::class.java)\n    private val PMODULE = ClassName.get(PModule::class.java)\n    private val PCLASS = ClassName.get(PClass::class.java)\n    private val PATTERN = ClassName.get(Pattern::class.java)\n    private val URI = ClassName.get(java.net.URI::class.java)\n    private val VERSION = ClassName.get(Version::class.java)\n\n    private const val PROPERTY_PREFIX: String = \"org.pkl.config.java.mapper.\"\n\n    private fun toClassName(fqn: String): ClassName {\n      val index = fqn.lastIndexOf(\".\")\n      if (index == -1) {\n        throw JavaCodeGeneratorException(\n          \"\"\"\n            Annotation `$fqn` is not a valid Java class.\n            The name of the annotation should be the canonical Java name of the class, for example, `com.example.Foo`.\n          \"\"\"\n            .trimIndent()\n        )\n      }\n      val packageName = fqn.substring(0, index)\n      val classParts = fqn.substring(index + 1).split('$')\n      return if (classParts.size == 1) {\n        ClassName.get(packageName, classParts.first())\n      } else {\n        ClassName.get(packageName, classParts.first(), *classParts.drop(1).toTypedArray())\n      }\n    }\n  }\n\n  val output: Map<String, String>","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/JavaCodeGenerator.kt#L112-L148","documentation":"JavaCodeGenerator.toClassName validates that each configured annotation FQN is a canonical Java class name containing at least one dot separating package from class name. A name with no '.' (e.g. `Foo`) cannot be split into package + class, so generation aborts with this message.","triggerScenarios":"In the pkl-java codegen config, an `annotations` mapping key (or similar FQN setting) is a bare class name like `Foo` or `Override` without a package, so lastIndexOf(\".\") returns -1.","commonSituations":"Copy-pasting a simple class name from Java source, configuring java.lang annotations without their package (java.lang.Override), or typos dropping the package prefix.","solutions":["Prefix the annotation with its full package, e.g. `com.example.Foo` or `java.lang.Override`.","Check the codegen config section (`annotations { ... }`) for keys missing dots.","If using a default-package class, move the class into a package — JavaPoet requires a package.","Re-run generation after fixing the FQN."],"exampleFix":"// before\nannotations { [\"Deprecated\"] = null }\n// after\nannotations { [\"java.lang.Deprecated\"] = null }","handlingStrategy":"validation","validationCode":"fun isValidJavaFqn(fqn: String) = fqn.contains('.') && fqn.split('.').all { it.matches(Regex(\"[A-Za-z_$][A-Za-z0-9_$]*\")) }","typeGuard":"fun String?.asCanonicalJavaName(): String? = this?.takeIf { it.lastIndexOf('.') > 0 }","tryCatchPattern":"try { generate() } catch (e: JavaCodeGeneratorException) { if (\"not a valid Java class\" in e.message!!) fixAnnotationFqn() else throw e }","preventionTips":["Always use fully-qualified names including java.lang prefix in annotation configs","Lint codegen config keys for a dot separator","Copy FQNs from imports, not class declarations"],"tags":["codegen","annotations","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}