{"record":{"id":"ee3f402a6a6291b7","repo":"HMCL-dev/HMCL","slug":"no-candidate-url","errorCode":null,"errorMessage":"No candidate URL","messagePattern":"No candidate URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java","lineNumber":377,"sourceCode":"        }\n        return readFullyAsString(connection);\n    }\n\n    public static String doGet(List<URI> uris) throws IOException {\n        List<IOException> exceptions = null;\n        for (URI uri : uris) {\n            try {\n                return doGet(uri);\n            } catch (IOException e) {\n                if (exceptions == null) {\n                    exceptions = new ArrayList<>(1);\n                }\n                exceptions.add(e);\n            }\n        }\n\n        if (exceptions == null) {\n            throw new IOException(\"No candidate URL\");\n        } else if (exceptions.size() == 1) {\n            throw exceptions.get(0);\n        } else {\n            IOException exception = new IOException(\"Failed to doGet\");\n            for (IOException e : exceptions) {\n                exception.addSuppressed(e);\n            }\n            throw exception;\n        }\n    }\n\n    public static String doPost(URI uri, String post) throws IOException {\n        return doPost(uri, post, \"application/x-www-form-urlencoded\");\n    }\n\n    public static String doPost(URI u, Map<String, String> params) throws IOException {\n        StringBuilder sb = new StringBuilder();\n        if (params != null) {","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java#L359-L395","documentation":"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.","triggerScenarios":"Calling download/GET APIs with an empty or unset list of mirror/base URLs, e.g. an empty repositories/downloadProviders configuration.","commonSituations":"Settings files where all mirrors were removed or never configured; version- or channel-specific URL lists that resolve to nothing; constructors given empty collections.","solutions":["Provide at least one valid base/mirror URL in the configuration","Restore default download sources (e.g. re-add the official HMCL API URLs)","Validate the URL list is non-empty before invoking the download API","Inspect settings/JSON files for wiped or misparsed URL arrays"],"exampleFix":"// before\nNetworkUtils.doGet(Collections.emptyList(), \"assets/index.json\");\n// after\nif (urls.isEmpty()) throw new IllegalArgumentException(\"at least one mirror URL required\");\nNetworkUtils.doGet(urls, \"assets/index.json\");","handlingStrategy":"validation","validationCode":"if (candidates == null || candidates.isEmpty())\n    throw new IllegalArgumentException(\"no download URLs configured\");","typeGuard":null,"tryCatchPattern":"try {\n    NetworkUtils.doGet(urls, path);\n} catch (IOException e) {\n    if (e.getMessage().equals(\"No candidate URL\")) {\n        // load default mirrors from config defaults\n    } else throw e;\n}","preventionTips":["Always seed configurations with at least one default mirror","Validate URL lists are non-empty at settings load time","Guard against JSON/config parsers silently dropping empty arrays"],"tags":["network","http","configuration"],"backgroundTag":"missing-required-config-field","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}