HMCL-dev/HMCL · error · IOException
Missing download URI:
Error message
Missing download URI:
What it means
The Disco download flow requires a direct download URI in the resolved package info. If DiscoRemoteFileInfo.directDownloadUri() is blank, the dialog throws IOException("Missing download URI: " + json) because it has nowhere to download the JDK archive from.
Solutions
- Retry with a different Java distribution or version in the dialog
- Inspect the JSON in the error message to confirm directDownloadUri is absent
- Download the JDK manually from the vendor site and add it via Java management
- Update HMCL in case the upstream API changed field names/semantics
Example fix
// before
if (StringUtils.isBlank(fileInfo.directDownloadUri()))
throw new IOException("Missing download URI: " + json);
// after
if (StringUtils.isBlank(fileInfo.directDownloadUri()))
throw new IOException("Missing download URI for " + version.getDistributionName()
+ "; try a different distribution or download manually"); Defensive patterns
Strategy: validation
Validate before calling
// before initiating the download task
JsonObject info = results.get(0).getAsJsonObject();
String uri = info.has("directDownloadUri") && !info.get("directDownloadUri").isJsonNull()
? info.get("directDownloadUri").getAsString() : null;
if (StringUtils.isBlank(uri)) {
throw new IllegalStateException("Disco entry has no direct download URI; pick another distribution");
} Try / catch
try {
downloadJava(version);
} catch (IOException e) {
if (e.getMessage().startsWith("Missing download URI:")) {
openVendorDownloadPage(version.getDistributionName());
} else throw e;
} Prevention
- Choose distributions that publish direct download links on foojay
- Fallback to manual vendor download when metadata lacks a URI
- Cache last-known-good metadata to detect upstream regressions
When it happens
Trigger: Downloading Java via the Disco API when the returned package entry has an empty/absent directDownloadUri field — the API resolved the query but returned only metadata without a direct link.
Common situations: Distributions that only expose indirect download pages; upstream API response shape changes; network proxies/CDN stripping fields; requests for combinations where foojay has no direct artifact link.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Unsupported checksum type:
- Failed to download texture
- Fabric metadata is invalid
- Game processor file not found, should be downloaded in…
- Game processor dependency missing
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/f9e0191e63f31c8f.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaDownloadDialog.java:387
DiscoJavaRemoteVersion version = remoteVersionBox.getSelectionModel().getSelectedItem();
JavaPackageType packageType = packageTypeBox.getSelectionModel().getSelectedItem();
if (version == null)
return;
Controllers.taskDialog(new GetTask(downloadProvider.injectURLWithCandidates(version.getLinks().pkgInfoUri()))
.setExecutor(Schedulers.io())
.thenComposeAsync(json -> {
DiscoResult<DiscoRemoteFileInfo> result = JsonUtils.fromNonNullJson(json, DiscoResult.typeOf(DiscoRemoteFileInfo.class));
if (result.getResult().size() != 1)
throw new IOException("Illegal result: " + json);
DiscoRemoteFileInfo fileInfo = result.getResult().get(0);
if (StringUtils.isNotBlank(fileInfo.checksumType())
&& !fileInfo.checksumType().equals("sha1") && !fileInfo.checksumType().equals("sha256") && !fileInfo.checksumType().equals("md5"))
throw new IOException("Unsupported checksum type: " + fileInfo.checksumType());
if (StringUtils.isBlank(fileInfo.directDownloadUri()))
throw new IOException("Missing download URI: " + json);
Path targetFile = Files.createTempFile("hmcl-java-", "." + version.getArchiveType());
targetFile.toFile().deleteOnExit();
Task<FileDownloadTask.IntegrityCheck> getIntegrityCheck;
if (StringUtils.isNotBlank(fileInfo.checksum()))
getIntegrityCheck = Task.completed(new FileDownloadTask.IntegrityCheck(fileInfo.checksumType(), fileInfo.checksum()));
else if (StringUtils.isNotBlank(fileInfo.checksumUri()))
getIntegrityCheck = new GetTask(downloadProvider.injectURLWithCandidates(fileInfo.checksumUri()))
.thenApplyAsync(checksum -> {
checksum = checksum.trim();
int idx = checksum.indexOf(' ');
if (idx > 0)
checksum = checksum.substring(0, idx);
return new FileDownloadTask.IntegrityCheck(fileInfo.checksumType(), checksum);
});View on GitHub (pinned to 24702dc5a0)