Skip to content

java: major-only version fails to resolve once a vendor publishes a four-component release (21.0.12.1 → 21.0.12+1-ms) #1712

Description

@become-iron
  • Feature: ghcr.io/devcontainers/features/java:1
  • Affected versions: 1.8.1 and 1.8.0 — their resolution code is identical
  • Environment: base image mcr.microsoft.com/devcontainers/base:ubuntu-24.04, SDKMAN script 5.23.0

Problem

"version": "21" stopped resolving. The install fails with:

Check if OpenJDK is available for version 21 for ms Distro
JDK version 21 is available in ms...
JDK_DISTRO: ms
Version 21 not found. Available versions:

Nothing is listed after that last line. Look at the two lines above it: the feature first says major 21 is available, then cannot find it.

Why

On 2026-08-19, vendors released JDK 21.0.12.1. SDKMAN puts the fourth number after a +, so Microsoft's build is now listed as 21.0.12+1-ms:

 Microsoft      |     | 25.0.4+1           | 25.0.4+1-ms
                |     | 21.0.12+1          | 21.0.12+1-ms
                |     | 17.0.20+1          | 17.0.20+1-ms
                |     | 11.0.32+1          | 11.0.32+1-ms

These are real builds, not renamed ones. 21.0.12+1-ms downloads microsoft-jdk-21.0.12.1-linux-x64.tar.gz, and 21.0.12+1.1-tem downloads Temurin's jdk-21.0.12.1+1.

The search pattern accepts three numbers at most:

${major_version}\.?[0-9]*\.?[0-9]*${suffix}${JDK_DISTRO}

There is no room for +1, so it matches nothing and the candidate list ends up empty.

One check runs before that search. It keeps only digits and dots, using [0-9]+(\.[0-9]+(\.[0-9]+)?)?, which reads 21.0.12 and matches. So the feature decides ms has major 21, does not fall back to tem, and fails on the next step instead.

(Both patterns live in find_version_list. The call site passes prefix, suffix and full_version_check. Line links are pinned to 99a3f1c, the commit released as 1.8.1.)

Reproduce

{
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
  "features": { "ghcr.io/devcontainers/features/java:1": { "version": "21" } }
}

Or without a build, against the same list the feature reads:

curl -s 'https://api.sdkman.io/2/candidates/java/linuxx64/versions/list?current=&installed=' \
  | grep -oP '\s*\K21\.?[0-9]*\.?[0-9]*(\.[a-z0-9]+)*-ms\s*'
# no output

What is affected

version jdkDistro Result
21, 17, 11, 25 ms (default) fails
21 tem fails, 21.0.12+1.1-tem
26 open fails, 26.0.2+1.1-open
21 librca, sapmchn fails
lts ms (default) installs the wrong JDK, no error — see below
latest ms (default) works, by luck: ms has no major 26, so it falls back to Temurin's 26.0.2-tem, which has no fourth number
latest open falls back to LTS and installs 25.0.2-open
21.0.12+1-ms any works, because a full identifier skips the search

amzn and oracle are fine today.

lts is worth a look on its own, because it stays quiet. Its branch takes the first entry of the list, then leaves the if/elif chain. It never reaches the empty-value check that raises the error. So the version stays empty, the unquoted argument disappears, and plain sdk install java installs SDKMAN's default — 25.0.4-tem right now. Ask for LTS, get another vendor, hear nothing.

Suggested fix

Accept the fourth number in the search:

-        regex="${prefix}\\K${major_version}\\.?[0-9]*\\.?[0-9]*${suffix}${JDK_DISTRO}\\s*"
+        regex="${prefix}\\K${major_version}\\.?[0-9]*\\.?[0-9]*(\\+[0-9]+(\\.[0-9]+)*)?${suffix}${JDK_DISTRO}\\s*"

I ran this against the live list for every distro, majors 8 through 26:

  • fixes ms, tem 21, open 26, librca, sapmchn
  • still matches plain identifiers such as 11.0.32-tem, 21.0.12-amzn, 26.0.2-tem
  • changes one result: zulu major 8 moves from 8.0.502.fx-zulu to 8.0.504+1-zulu, i.e. from the JavaFX bundle to the newer plain build
  • does not cover a fourth number introduced by a hyphen, such as 21.0.12-crac+1.2-librca. Those do not match today either, so nothing gets worse.

Two more things the same sweep found

jdkDistro: graal with version: 25 resolves to 25.2.4-graal, which does not exist. The pattern matches the GraalVM CE row 25.2.4-graalce and cuts off the ce. sort -rV | head -1 picks it, the membership check lets it through, and sdk install fails. Same shape of problem: a pattern loose enough to invent an identifier.

The availability check looks for the bare text ${JDK_DISTRO} anywhere in the table. ms works only because no other vendor row contains those two letters.

One idea, if you want it: versions/all returns the same identifiers as a plain comma-separated list, with no columns or headers to parse. The table you parse now was reformatted upstream a few days ago.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions