Devolutions/UniGetUI · error · InvalidDataException

The updater download server returned an invalid partial cont

Error message

The updater download server returned an invalid partial content range.

What it means

DownloadInstallerPartAsync requests a Range resume when a valid partial file and metadata exist. If the server responds with HTTP 206 Partial Content, IsValidPartialResponse checks that the Content-Range header's From value equals the current partialLength. When it does not, the engine normally deletes the partial and restarts (allowRestart=true). The InvalidDataException is only thrown on the recursive call where allowRestart is false — meaning the server returned a bad range twice in a row. This signals a genuinely broken CDN/mirror or a corrupted partial that cannot be trusted.

Source

Thrown at src/UniGetUI.Core.Tools/UpdaterDownloadEngine.cs:194

            DeletePartialDownload(destinationPath, log);
            return await DownloadInstallerPartAsync(
                client,
                identity,
                destinationPath,
                allowRestart: false,
                log,
                cancellationToken
            );
        }

        response.EnsureSuccessStatusCode();

        bool appendToPartial = response.StatusCode is HttpStatusCode.PartialContent;
        if (appendToPartial && !IsValidPartialResponse(response, partialLength))
        {
            if (!allowRestart)
            {
                throw new InvalidDataException(
                    "The updater download server returned an invalid partial content range."
                );
            }

            log?.Invoke("Updater download returned an invalid partial response; restarting.");
            DeletePartialDownload(destinationPath, log);
            return await DownloadInstallerPartAsync(
                client,
                identity,
                destinationPath,
                allowRestart: false,
                log,
                cancellationToken
            );
        }

        if (appendToPartial && !IsResumeValidatorCompatible(metadata, response))
        {

View on GitHub (pinned to 9b1d7d0eab)

Solutions

  1. Delete the .part file and its .part.json metadata manually, then retry the download from scratch.
  2. Verify the download URL points to a server/CDN that correctly supports HTTP Range requests and returns accurate Content-Range headers.
  3. Check that the partial file was not externally modified between the initial download and the resume attempt.
  4. If behind a proxy, bypass it or reconfigure it to pass Range headers and Content-Range responses through unchanged.

Example fix

// before: partial file present but range mismatch keeps failing
UpdaterDownloadEngine.DownloadInstallerPartAsync(client, identity, destPath)
// after: clean residual partial state then retry
UpdaterDownloadEngine.DeletePartialDownload(destPath);
await UpdaterDownloadEngine.DownloadInstallerPartAsync(client, identity, destPath);
Defensive patterns

Strategy: retry

Validate before calling

if (File.Exists(UpdaterDownloadEngine.GetPartialPath(destPath)))
{
    long partialLen = new FileInfo(UpdaterDownloadEngine.GetPartialPath(destPath)).Length;
    if (partialLen == 0) UpdaterDownloadEngine.DeletePartialDownload(destPath);
}

Try / catch

try { await UpdaterDownloadEngine.DownloadInstallerPartAsync(client, identity, destPath); }
catch (InvalidDataException ex) when (ex.Message.Contains("invalid partial content range"))
{ UpdaterDownloadEngine.DeletePartialDownload(destPath); /* retry fresh */ }

Prevention

When it happens

Trigger: A resume request (Range: bytes=N-) is sent. The server returns 206 but the Content-Range start byte differs from N. The engine deletes the partial, retries from byte 0 with allowRestart=false, and the server again returns 206 with a mismatched range start instead of a clean 200 or a correct 206. This requires a server that returns 206 status with inconsistent range data across two sequential requests.

Common situations: A reverse proxy or CDN rewrites Range responses incorrectly. The partial file was truncated externally and the metadata's recorded length drifted from the real file size. A load balancer routes the retry to a different backend that disagrees about the content range. The server does not support Range properly but advertises 206 anyway.

Related errors


AI-assisted analysis of Devolutions/UniGetUI@9b1d7d0eab (2026-08-13). Data as JSON: /api/errors/c4f1ed949d77352c. Report an issue: GitHub.