apple/pkl · error · KotlinCodeGeneratorException
Cannot generate Kotlin enum class for Pkl type alias
Error message
Cannot generate Kotlin enum class for Pkl type alias `${typeAlias.displayName}` because string literal types "$firstLiteral" and "$secondLiteral" would both be converted to enum constant name `$enumConstantName`. What it means
When building a Kotlin enum for a Pkl string-literal type alias, two distinct literals can normalize to the same enum constant name (e.g. "foo-bar" and "foo_bar"). Since enum constants must be unique, generateEnumTypeSpec throws this KotlinCodeGeneratorException during grouping/reduction.
Solutions
- Rename one of the conflicting literals in the Pkl alias so each maps to a unique enum constant name.
- Model the values as a plain String property instead of a string-literal alias.
- Keep literals distinguishable after normalization (differ in letters/digits, not just separators or case).
Example fix
// before (Pkl) typealias Path = "a/b" | "a:b" // both -> A_B // after typealias Path = "slash" | "colon"
Defensive patterns
Strategy: validation
Validate before calling
val names = literals.map { CodeGeneratorUtils.toEnumConstantName(it) }
require(names.size == names.toSet().size) {
"Enum constant name collision among literals: $literals"
} Type guard
fun hasUniqueEnumNames(literals: Set<String>): Boolean =
literals.mapNotNull { CodeGeneratorUtils.toEnumConstantName(it) }.toSet().size == literals.size Try / catch
try {
generator.generate(module)
} catch (e: KotlinCodeGeneratorException) {
if (e.message?.contains("would both be converted to enum constant name") == true) {
logger.error("Rename one of the colliding literals: ${e.message}")
} else throw e
} Prevention
- Keep alias literals distinguishable after case/separator normalization
- Avoid using separators like '-', '_', '/', ':' as the only difference between literals
- Run a uniqueness check over toEnumConstantName results in CI
When it happens
Trigger: A type alias with two string literals whose toEnumConstantName results collide, e.g. `typealias Sep = "/"|"//"` or `"a-b"|"a_b"`.
Common situations: Aliases containing separators, case variants, or punctuation that normalize identically when converted to SCREAMING_SNAKE_CASE enum names.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Cannot generate Kotlin enum class for Pkl type alias
- Cannot generate Java enum class for Pkl type alias
- Cannot generate Java enum class for Pkl type alias
- Cannot generate Kotlin code for a Pkl standard library…
- I/O error writing file `$outputFile`. Cause
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/caf051c9e47125e3.
Report an issue: GitHub.
Appendix: source
Thrown at pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/KotlinCodeGenerator.kt:557
return if (superclass == null && !pClass.isAbstract && !pClass.isOpen) generateDataClass()
else generateRegularClass()
}
private fun generateEnumTypeSpec(
typeAlias: TypeAlias,
stringLiterals: Set<String>,
): TypeSpec.Builder {
val enumConstantToPklNames =
stringLiterals
.groupingBy { literal ->
CodeGeneratorUtils.toEnumConstantName(literal)
?: throw KotlinCodeGeneratorException(
"Cannot generate Kotlin enum class for Pkl type alias `${typeAlias.displayName}` " +
"because string literal type \"$literal\" cannot be converted to a valid enum constant name."
)
}
.reduce { enumConstantName, firstLiteral, secondLiteral ->
throw KotlinCodeGeneratorException(
"Cannot generate Kotlin enum class for Pkl type alias `${typeAlias.displayName}` " +
"because string literal types \"$firstLiteral\" and \"$secondLiteral\" " +
"would both be converted to enum constant name `$enumConstantName`."
)
}
val builder =
TypeSpec.enumBuilder(typeAlias.simpleName)
.primaryConstructor(
FunSpec.constructorBuilder().addParameter("value", String::class).build()
)
.addProperty(PropertySpec.builder("value", String::class).initializer("value").build())
.addFunction(
FunSpec.builder("toString")
.addModifiers(KModifier.OVERRIDE)
.addStatement("return value")
.build()
)View on GitHub (pinned to f3efcbfc9b)