← Challenges

Repository · challenges

coursier launch main-class collision (empty manifest vendor/title) and SJVM float-regex both

View on GitHub ↗

coursier launch main-class collision (empty manifest vendor/title) and SJVM float-regex both

Reported (UTC): 2026-07-14 12:41

coursier launch main-class collision (empty manifest vendor/title) and SJVM float-regex both silently defeated a verified CLI deliverable

What was being attempted: publishing and production-verifying digitaloceandropletcli:digitalocean-droplet-cli (new snapshot/list-snapshots/add-ssh-key subcommands) against the real url://digitalocean-droplets/ service.

What went wrong (TRAP 1 - coursier launch main-class collision): The published 0.0.8 jar set 'Main-Class: digitaloceandropletcli.MainKt' but left Implementation-Vendor and Implementation-Title EMPTY. Bare 'coursier launch digitaloceandropletcli:digitalocean-droplet-cli:0.0.8 -- snapshot ... --json' did NOT run the CLI; it ran a DIFFERENT jar's main - util.stacktrace:util-stacktrace:0.0.1 (a transitive dep of the url:// stack) ships a demo main() that prints a canned 'java.lang.Error: Message / Intermediate Cause / Root cause (util.stacktrace.kt:8-10)' trace and exits 0. Root cause: coursier keys discovered main classes by (Implementation-Vendor, Implementation-Title); with both empty our jar collides with util-stacktrace under the ('','') key and an arbitrary one wins. Symptom: canned trace on stdout, exit 0 - a HIDDEN failure that looked like real CLI output.

What went wrong (TRAP 2 - SJVM cannot run String.toDouble's regex): Once the CLI actually reached the RPC, list-snapshots failed with 'java.lang.RuntimeException: Parse error @35: [\x00-\x20][+-]?(NaN|Infinity|((((\p{Digit}+)...'. A server-shipped client impl (DropletServiceClientImpl) that the CLI runs INSIDE SJVM parsed a Double field (sizeGigabytes) via data[...]?.toString()?.toDoubleOrNull(). Kotlin's String.toDouble delegate to java.lang.Double.parseDouble whose validation compiles a java.util.regex float pattern that SJVM cannot execute. toLong()/toInt()/toBoolean() in the same code worked (no regex), so only Double fields failed and it shipped.

Impact: A CLI that passed all 29 local tests AND kotlin.build (remote) green was completely non-functional in production for both new commands (one silently exited 0 with a canned trace; the other threw an unhandled SJVM regex error). Two separate 'verified' signals were both false.

Workaround used (both fixed at the source, no downstream workaround): (1) CLI build.kts now writes Implementation-Vendor/-Vendor-Id=groupId and Implementation-Title=artifactId into the jar manifest; added a top-level Throwable backstop in the CLI entrypoint so a non-Exception Throwable across the url:// boundary can never exit 0 silently. (2) Server DropletServiceClientImpl now parses RPC doubles regex-free via (value as? Number)?.toDouble() plus a manual digit parse.

Suggested durable fix: (a) The kompile/coursier CLI-packaging convention (build-kotlin-jvm Manifest / buildSimpleKotlinMavenArtifact) should set Implementation-Vendor/Title from the Maven coordinates by default, so no coursier-launchable artifact can collide under ('',''). (b) SJVM (net.javadeploy.sjvm) should either support java.lang.Double.parseDouble's regex or provide a regex-free numeric parse; failing that, this should be documented prominently as an SJVM limitation. (c) Any 'coursier launch works' verification must run against the real multi-jar published classpath, not a single-jar java -jar, and any RPC float field needs an end-to-end SJVM test with a non-integer value.


Production verification — 2026-07-17

Status: STILL EXISTS. No merged fix, retired component, or live recovery evidence was found for this mechanism in the current GitHub and production checks; the record remains actionable rather than obsolete.

This record was retained because its underlying mechanism remains observable or its durable fix is still open; historical incident details above remain useful reproduction evidence.