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

  1. Rename one of the conflicting literals in the Pkl alias so each maps to a unique enum constant name.
  2. Model the values as a plain String property instead of a string-literal alias.
  3. 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

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


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)