- 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.
ghcr.io/devcontainers/features/java:1mcr.microsoft.com/devcontainers/base:ubuntu-24.04, SDKMAN script 5.23.0Problem
"version": "21"stopped resolving. The install fails with: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 as21.0.12+1-ms:These are real builds, not renamed ones.
21.0.12+1-msdownloadsmicrosoft-jdk-21.0.12.1-linux-x64.tar.gz, and21.0.12+1.1-temdownloads Temurin'sjdk-21.0.12.1+1.The search pattern accepts three numbers at most:
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 reads21.0.12and matches. So the feature decidesmshas major 21, does not fall back totem, and fails on the next step instead.(Both patterns live in
find_version_list. The call site passesprefix,suffixandfull_version_check. Line links are pinned to99a3f1c, 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:
What is affected
versionjdkDistro21,17,11,25ms(default)21tem21.0.12+1.1-tem26open26.0.2+1.1-open21librca,sapmchnltsms(default)latestms(default)mshas no major 26, so it falls back to Temurin's26.0.2-tem, which has no fourth numberlatestopen25.0.2-open21.0.12+1-msamznandoracleare fine today.ltsis worth a look on its own, because it stays quiet. Its branch takes the first entry of the list, then leaves theif/elifchain. It never reaches the empty-value check that raises the error. So the version stays empty, the unquoted argument disappears, and plainsdk install javainstalls SDKMAN's default —25.0.4-temright now. Ask for LTS, get another vendor, hear nothing.Suggested fix
Accept the fourth number in the search:
I ran this against the live list for every distro, majors 8 through 26:
ms,tem21,open26,librca,sapmchn11.0.32-tem,21.0.12-amzn,26.0.2-temzulumajor 8 moves from8.0.502.fx-zuluto8.0.504+1-zulu, i.e. from the JavaFX bundle to the newer plain build21.0.12-crac+1.2-librca. Those do not match today either, so nothing gets worse.Two more things the same sweep found
jdkDistro: graalwithversion: 25resolves to25.2.4-graal, which does not exist. The pattern matches the GraalVM CE row25.2.4-graalceand cuts off thece.sort -rV | head -1picks it, the membership check lets it through, andsdk installfails. 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.msworks only because no other vendor row contains those two letters.One idea, if you want it:
versions/allreturns 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.