Symptom

The “Deploy Debian Repo” workflow (self-hosted runner s1) failed every run at the Build WASM step:

1
2
3
4
> Task :composeApp:compileDevelopmentExecutableKotlinWasmJs FAILED
e: java.lang.NullPointerException: Cannot invoke
   "...IrMangleComputer.getTypeParameterContainers()" because "$this" is null
e: Compiler terminated with internal error

The same command passed on a developer laptop. The per-module compiles (:shared/:composeApp:compileKotlinWasmJs) were FROM-CACHE; only the executable-link step (which consumes those cached klibs) crashed.

Root cause

A corrupt/stale wasm klib in s1’s persistent shared Gradle build cache (~/.gradle/caches/build-cache-1, org.gradle.caching=true). s1 is a long-lived self-hosted runner, so GRADLE_USER_HOME persists across runs (and is shared with other workflows + manual builds). The WASM step has timeout-minutes: 15; a build killed mid-write (timeout, or a colliding build) can leave a partial/incompatible klib in the build cache. Every later run then restores it (FROM-CACHE) and the IR linker NPEs on it. The laptop has a locally-consistent cache, so it passes.

Confirmed by reproduction: building the failing task on s1 with an isolated, empty GRADLE_USER_HOME (--gradle-user-home /tmp/... --no-build-cache) succeeded in 2m51s — proving the source/JDK/RAM are fine and only the cached artifact was bad. (s1 has JDK 21 available and 125G RAM; not a JDK or OOM issue.)

Fix

Added --no-build-cache to every ./gradlew build invocation in the package-debian job (wasm, server proguard, desktop uber-jar, krill-pi4j and krill-mcp shadowJars). A release/deploy build must compile fresh rather than trust a corruptible persistent cache. Note: gradle/actions/setup-gradle’s cache-disabled would not fix this — on a self-hosted runner the local ~/.gradle/caches persists regardless, and org.gradle.caching=true reads it; only --no-build-cache on the invocation bypasses it.

Prevention

On self-hosted runners with a persistent GRADLE_USER_HOME, treat the build cache as untrusted for release builds — --no-build-cache (or a clean home). When a Kotlin/Wasm “Internal compiler error” / IrMangleComputer NPE appears only in CI and only on FROM-CACHE tasks, suspect a corrupt cached klib, not the source. To clear it manually when the runner is idle: rm -rf ~/.gradle/caches/build-cache-1. Consider also raising the WASM timeout-minutes so a slow build isn’t killed mid-cache-write.