{"record":{"id":"caf051c9e47125e3","repo":"apple/pkl","slug":"cannot-generate-kotlin-enum-class-for-pkl-type-ali-caf051","errorCode":null,"errorMessage":"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`.","messagePattern":"Cannot generate Kotlin enum class for Pkl type alias `(.+?)` because string literal types \"\\$firstLiteral\" and \"\\$secondLiteral\" would both be converted to enum constant name `\\$enumConstantName`\\.","errorType":"exception","errorClass":"KotlinCodeGeneratorException","httpStatus":null,"severity":"error","filePath":"pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/KotlinCodeGenerator.kt","lineNumber":557,"sourceCode":"    return if (superclass == null && !pClass.isAbstract && !pClass.isOpen) generateDataClass()\n    else generateRegularClass()\n  }\n\n  private fun generateEnumTypeSpec(\n    typeAlias: TypeAlias,\n    stringLiterals: Set<String>,\n  ): TypeSpec.Builder {\n    val enumConstantToPklNames =\n      stringLiterals\n        .groupingBy { literal ->\n          CodeGeneratorUtils.toEnumConstantName(literal)\n            ?: throw KotlinCodeGeneratorException(\n              \"Cannot generate Kotlin enum class for Pkl type alias `${typeAlias.displayName}` \" +\n                \"because string literal type \\\"$literal\\\" cannot be converted to a valid enum constant name.\"\n            )\n        }\n        .reduce { enumConstantName, firstLiteral, secondLiteral ->\n          throw KotlinCodeGeneratorException(\n            \"Cannot generate Kotlin enum class for Pkl type alias `${typeAlias.displayName}` \" +\n              \"because string literal types \\\"$firstLiteral\\\" and \\\"$secondLiteral\\\" \" +\n              \"would both be converted to enum constant name `$enumConstantName`.\"\n          )\n        }\n\n    val builder =\n      TypeSpec.enumBuilder(typeAlias.simpleName)\n        .primaryConstructor(\n          FunSpec.constructorBuilder().addParameter(\"value\", String::class).build()\n        )\n        .addProperty(PropertySpec.builder(\"value\", String::class).initializer(\"value\").build())\n        .addFunction(\n          FunSpec.builder(\"toString\")\n            .addModifiers(KModifier.OVERRIDE)\n            .addStatement(\"return value\")\n            .build()\n        )","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-codegen-kotlin/src/main/kotlin/org/pkl/codegen/kotlin/KotlinCodeGenerator.kt#L539-L575","documentation":"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.","triggerScenarios":"A type alias with two string literals whose toEnumConstantName results collide, e.g. `typealias Sep = \"/\"|\"//\"` or `\"a-b\"|\"a_b\"`.","commonSituations":"Aliases containing separators, case variants, or punctuation that normalize identically when converted to SCREAMING_SNAKE_CASE enum names.","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)."],"exampleFix":"// before (Pkl)\ntypealias Path = \"a/b\" | \"a:b\" // both -> A_B\n// after\ntypealias Path = \"slash\" | \"colon\"","handlingStrategy":"validation","validationCode":"val names = literals.map { CodeGeneratorUtils.toEnumConstantName(it) }\nrequire(names.size == names.toSet().size) {\n  \"Enum constant name collision among literals: $literals\"\n}","typeGuard":"fun hasUniqueEnumNames(literals: Set<String>): Boolean =\n  literals.mapNotNull { CodeGeneratorUtils.toEnumConstantName(it) }.toSet().size == literals.size","tryCatchPattern":"try {\n  generator.generate(module)\n} catch (e: KotlinCodeGeneratorException) {\n  if (e.message?.contains(\"would both be converted to enum constant name\") == true) {\n    logger.error(\"Rename one of the colliding literals: ${e.message}\")\n  } else throw e\n}","preventionTips":["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"],"tags":["kotlin","codegen","enum","name-collision"],"backgroundTag":"invalid-enum-value","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"}