Fixing JDK 21 jpackage and WiX Toolset Exit Code 14 During Windows EXE Creation
Understanding the WiX candle.exe Exit Code 14 Error
When packaging desktop applications using JDK 21's jpackage—frequently via Gradle and frameworks like JetBrains Compose Multiplatform—generating a Windows EXE installer (--type exe) relies heavily on the WiX Toolset (v3.11). Under the hood, jpackage first builds an MSI installer and then wraps it inside an EXE bootstrapper using WiX's Burn engine.
During this bootstrap phase, jpackage generates a WiX file named bundle.wxf and compiles it using candle.exe. If this command suddenly fails with:
java.io.IOException: Command [candle.exe, -nologo, ...\bundle.wxf, -ext, WixUtilExtension, -arch, x64, -out, ...\bundle.wixobj] exited with 14 codeIt means candle.exe encountered a fatal XML parsing or schema validation error. Unlike minor compiler warnings or reference errors (which usually throw exit codes 1 or 2), exit code 14 indicates that the generated XML file could not be parsed into a valid DOM or syntax tree.
Root Causes of Exit Code 14 in bundle.wxf
Because bundle.wxf is generated dynamically by jpackage, several subtle environment and metadata issues can corrupt the file syntax:
- Unescaped XML Characters in Application Metadata: If your
appName,vendor,copyright, ordescriptionattributes contain reserved XML entities like ampersands (&), quotation marks ("), angle brackets (<,>), or apostrophes ('),jpackagemay write them directly without escaping, creating invalid XML. - Invalid or Malformed GUID (Upgrade UUID): WiX Burn bundles strictly enforce the registry-style GUID format (
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX). If theupgradeUuidin your Gradle script is lowercase, lacks hyphens, or is improperly formatted,bundle.wxfvalidation will crashcandle.exe. - Missing .NET Framework 3.5 Runtime: WiX Toolset 3.11 Burn bootstrapper components rely on .NET Framework 3.5/2.0. If Windows 10 does not have the .NET 3.5 feature enabled, certain custom bootstrapper actions or extension hooks will crash prematurely.
- File Locking and Path Collisions: Antivirus engines or aggressive indexing services on Windows frequently lock temporary files during the rapid creation and compilation of
bundle.wxf, leading to partial writes (which explains the observed XML unexpected-end-of-file condition).
How to Diagnose and Resolve the Issue
1. Preserve jpackage Intermediate Files
By default, jpackage deletes the temporary build folder when the task finishes or fails. To capture the full bundle.wxf file and the raw WiX compiler diagnostic output, pass the --temp and --verbose arguments through your build configuration.
In Compose Multiplatform (build.gradle.kts), pass additional arguments to jpackage:
compose.desktop {\n application {\n mainClass = "com.drivesweep.MainKt"\n nativeDistributions {\n targetFormats(\n org.jetbrains.compose.desktop.application.dsl.TargetFormat.Exe,\n org.jetbrains.compose.desktop.application.dsl.TargetFormat.Msi\n )\n windows {\n // Specify an explicit directory to inspect generated WiX sources\n freeCompilerArgs.addAll(\n "--verbose",\n "--temp", layout.buildDirectory.dir("tmp/jpackage").get().asFile.absolutePath\n )\n }\n }\n }\n}Once the task fails, navigate to build/tmp/jpackage/config/bundle.wxf and open it in an editor to inspect line-by-line syntax errors or identify where the XML structure broke.
2. Sanitize Metadata and Verify UUIDs
Ensure that all application properties configured in your Gradle script are strictly alphanumeric and do not contain special symbols:
compose.desktop {\n application {\n nativeDistributions {\n packageName = "DriveSweep"\n packageVersion = "1.0.0"\n description = "DriveSweep Disk Cleanup Utility" // Avoid &, <, >, \", etc.\n vendor = "DriveSweep Team"\n \n windows {\n menuGroup = "DriveSweep"\n // Ensure valid, uppercase GUID syntax with hyphens\n upgradeUuid = "9B1B8A44-245C-4F75-9C49-2A2F1D0A358B"\n }\n }\n }\n}3. Enable .NET Framework 3.5 on Windows 10
WiX 3.11's bootstrapper extensions call underlying Windows components that require .NET 3.5. Ensure this Windows Feature is enabled:
- Press Win + R, type
optionalfeatures, and hit Enter. - Check the box for .NET Framework 3.5 (includes .NET 2.0 and 3.0).
- Click OK and let Windows download the required binaries.
4. Alternative Workaround: Build an MSI Directly
WiX Burn (which creates .exe files) wraps an underlying .msi. In enterprise and professional deployment environments, deploying via MSI is standard and completely avoids the bundle.wxf compilation step.
To test if the core application packaging succeeds without the EXE wrapper, run:
.\gradlew.bat :composeApp:packageMsiIf packageMsi succeeds, the issue is strictly isolated to the Burn bootstrapper configuration in bundle.wxf.