apple/pkl · error · JavaCodeGeneratorException

Cannot generate Java enum class for Pkl type alias

Error message

Cannot generate Java 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

During enum generation, distinct string literals that sanitize to the same Java enum constant name would collide (e.g. "foo-bar" and "foo_bar" both becoming FOO_BAR). The reduce step detects the collision and aborts, because two enum constants cannot share a name.

Solutions

  1. Rename one of the conflicting literals in the Pkl module so they produce distinct enum names.
  2. Normalize your value set to one convention (e.g. all kebab-case, no case-only differences).
  3. Check which literals collide by applying Java identifier sanitization rules manually.
  4. Fall back to String representation for the alias if the values cannot be changed.

Example fix

// before (Pkl)
type Level = "log-level" | "log_level"
// after
type Level = "log-level" | "logLevel"
Defensive patterns

Strategy: validation

Validate before calling

val names = literals.map { CodeGeneratorUtils.toEnumConstantName(it) }; require(names.distinct().size == names.size) { "enum name collision: $literals" }

Type guard

fun Set<String>.hasEnumCollisions() = groupingBy { CodeGeneratorUtils.toEnumConstantName(it)!! }.eachCount().any { it.value > 1 }

Try / catch

try { generate() } catch (e: JavaCodeGeneratorException) { if ("would both be converted" in e.message!!) renameCollidingLiterals() else throw e }

Prevention

When it happens

Trigger: A string-literal type alias contains two literals that toEnumConstantName normalizes to the identical identifier — differing only in case, separators (-, ., spaces), or other characters stripped during sanitization.

Common situations: Modules mixing kebab-case and snake_case variants of the same word ("log-level" vs "log_level"), literals differing only in case, or values differing only by punctuation.

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/c1060b7b85b1cde7. Report an issue: GitHub.

Appendix: source

Thrown at pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/JavaCodeGenerator.kt:665

      .initializer("0L")
      .build()
  }

  private fun generateEnumTypeSpec(
    typeAlias: TypeAlias,
    stringLiterals: Set<String>,
  ): TypeSpec.Builder {
    val enumConstantToPklNames =
      stringLiterals
        .groupingBy { literal ->
          CodeGeneratorUtils.toEnumConstantName(literal)
            ?: throw JavaCodeGeneratorException(
              "Cannot generate Java 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 JavaCodeGeneratorException(
            "Cannot generate Java 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)
        .addModifiers(Modifier.PUBLIC)
        .addField(String::class.java, "value", Modifier.PRIVATE)
        .addMethod(
          MethodSpec.constructorBuilder()
            .addModifiers(Modifier.PRIVATE)
            .addParameter(String::class.java, "value")
            .addStatement("this.value = value")
            .build()
        )
        .addMethod(

View on GitHub (pinned to f3efcbfc9b)