HMCL-dev/HMCL · error

No candidate URL

Error message

No candidate URL

What it means

NetworkUtils.doGet iterates over candidate base URLs and collects IOExceptions; if the candidate list is empty (exceptions == null sentinel) it throws 'No candidate URL' because there was nothing to request at all.

Solutions

  1. Provide at least one valid base/mirror URL in the configuration
  2. Restore default download sources (e.g. re-add the official HMCL API URLs)
  3. Validate the URL list is non-empty before invoking the download API
  4. Inspect settings/JSON files for wiped or misparsed URL arrays

Example fix

// before
NetworkUtils.doGet(Collections.emptyList(), "assets/index.json");
// after
if (urls.isEmpty()) throw new IllegalArgumentException("at least one mirror URL required");
NetworkUtils.doGet(urls, "assets/index.json");
Defensive patterns

Strategy: validation

Validate before calling

if (candidates == null || candidates.isEmpty())
    throw new IllegalArgumentException("no download URLs configured");

Try / catch

try {
    NetworkUtils.doGet(urls, path);
} catch (IOException e) {
    if (e.getMessage().equals("No candidate URL")) {
        // load default mirrors from config defaults
    } else throw e;
}

Prevention

When it happens

Trigger: Calling download/GET APIs with an empty or unset list of mirror/base URLs, e.g. an empty repositories/downloadProviders configuration.

Common situations: Settings files where all mirrors were removed or never configured; version- or channel-specific URL lists that resolve to nothing; constructors given empty collections.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/ee3f402a6a6291b7. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java:377

        }
        return readFullyAsString(connection);
    }

    public static String doGet(List<URI> uris) throws IOException {
        List<IOException> exceptions = null;
        for (URI uri : uris) {
            try {
                return doGet(uri);
            } catch (IOException e) {
                if (exceptions == null) {
                    exceptions = new ArrayList<>(1);
                }
                exceptions.add(e);
            }
        }

        if (exceptions == null) {
            throw new IOException("No candidate URL");
        } else if (exceptions.size() == 1) {
            throw exceptions.get(0);
        } else {
            IOException exception = new IOException("Failed to doGet");
            for (IOException e : exceptions) {
                exception.addSuppressed(e);
            }
            throw exception;
        }
    }

    public static String doPost(URI uri, String post) throws IOException {
        return doPost(uri, post, "application/x-www-form-urlencoded");
    }

    public static String doPost(URI u, Map<String, String> params) throws IOException {
        StringBuilder sb = new StringBuilder();
        if (params != null) {

View on GitHub (pinned to 24702dc5a0)