HMCL-dev/HMCL · error · IOException

Failed to download theme background: HTTP

Error message

Failed to download theme background: HTTP ${responseCode} ${uri}

What it means

Thrown by downloadNetworkBackground when the HTTP response code for a theme-pack network background is not in the 2xx range. The method downloads the image to a temporary file and aborts immediately on any non-success status, propagating the code and URI in the message.

Solutions

  1. Open the URL in a browser to confirm the status; fix or replace stale 404/403 URLs in the manifest.
  2. Retry later if the server returned 5xx (transient upstream failure).
  3. Switch to an image hosted on a reliable HTTPS host, or bundle the image as a local asset in the pack.
  4. Check that any tokens embedded in the URL are still valid and not expired.

Example fix

// before
"background": { "url": "https://old.example.com/wall.png" } // 404
// after
"background": { "url": "https://cdn.example.com/assets/wall.png" }
Defensive patterns

Strategy: retry

Validate before calling

// Optionally HEAD-check the URL before export:
HttpURLConnection c = (HttpURLConnection) URI.create(url).toURL().openConnection();
c.setRequestMethod("HEAD");
if (c.getResponseCode() / 100 != 2) throw new IllegalStateException("background URL not downloadable: " + c.getResponseCode());

Try / catch

try {
    downloadNetworkBackground(uri, tmp);
} catch (IOException e) {
    if (e.getMessage().contains("HTTP 5")) {
        // transient server error: retry with backoff
    } else {
        // 4xx: fix or replace the URL
    }
}

Prevention

When it happens

Trigger: Resolving a network background during theme-pack export/import where the server returns 404, 403, 500, etc.; NetworkUtils.resolveConnection succeeds but connection.getResponseCode() / 100 != 2.

Common situations: The image URL in the theme manifest no longer exists (404); the host blocks automated downloads (403); a redirect ends at an error page; a CDN or mirror is temporarily failing (5xx); the URL requires authentication.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java:1543

            return new ThemeBackground.Image(entryName);
        } finally {
            if (!success) {
                try {
                    deleteIfExists(temporaryFile);
                } catch (IOException e) {
                    LOG.warning("Failed to delete temporary theme-pack asset: " + temporaryFile, e);
                }
            }
        }
    }

    /// Downloads one network background into a temporary file without installing it into the persistent image cache.
    private static void downloadNetworkBackground(URI uri, Path outputFile) throws IOException {
        HttpURLConnection connection = NetworkUtils.resolveConnection(NetworkUtils.createHttpConnection(uri));
        try {
            int responseCode = connection.getResponseCode();
            if (responseCode / 100 != 2) {
                throw new IOException("Failed to download theme background: HTTP " + responseCode + " " + uri);
            }

            ContentEncoding contentEncoding = ContentEncoding.fromConnection(connection);
            try (InputStream input = contentEncoding.wrap(connection.getInputStream());
                 OutputStream output = Files.newOutputStream(outputFile)) {
                IOUtils.copyTo(input, output, new byte[IOUtils.DEFAULT_BUFFER_SIZE]);
            }
        } finally {
            connection.disconnect();
        }
    }

    /// Returns a safe theme-pack asset file name for a downloaded network background.
    private static String networkBackgroundAssetName(URI uri) {
        String path = Objects.toString(uri.getPath(), "");
        @Nullable Path fileNamePath = path.isBlank() ? null : Path.of(path).getFileName();
        String fileName = fileNamePath != null ? fileNamePath.toString() : "";
        String sanitized = sanitizePathSegment(fileName);

View on GitHub (pinned to 24702dc5a0)