Devolutions/UniGetUI · error · InvalidDataException

The updater download server returned partial content with a

Error message

The updater download server returned partial content with a changed validator.

What it means

During a resume, if the server returns 206 Partial Content, IsResumeValidatorCompatible compares the stored metadata's ETag (or Last-Modified) against the response's current ETag/Last-Modified. If they differ the remote file changed mid-download. The engine deletes the partial and restarts when allowRestart is true. The InvalidDataException is thrown only on the recursive call (allowRestart=false), meaning the validator changed on both attempts — the asset is being mutated server-side between requests and a safe resume is impossible.

Source

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

            }

            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))
        {
            if (!allowRestart)
            {
                throw new InvalidDataException(
                    "The updater download server returned partial content with a changed validator."
                );
            }

            log?.Invoke("Updater download validator changed during resume; restarting.");
            DeletePartialDownload(destinationPath, log);
            return await DownloadInstallerPartAsync(
                client,
                identity,
                destinationPath,
                allowRestart: false,
                log,
                cancellationToken
            );
        }

        if (!appendToPartial && partialLength > 0)
        {

View on GitHub (pinned to 9b1d7d0eab)

Solutions

  1. Delete the .part and .part.json files, then re-download from scratch — the stored partial belongs to a superseded artifact.
  2. Confirm the download URL is stable (immutable release artifact) rather than a mutable 'latest' redirect target.
  3. If the asset is genuinely being replaced, wait for the deployment to settle and retry the update check to obtain a fresh UpdaterDownloadIdentity.
  4. Verify the mirror/CDN is not serving conflicting validators for the same URL.

Example fix

// before: resume fails because the remote artifact changed
UpdaterDownloadEngine.DownloadInstallerPartAsync(client, identity, destPath)
// after: discard the stale partial tied to the old validator
UpdaterDownloadEngine.DeletePartialDownload(destPath);
await UpdaterDownloadEngine.DownloadInstallerPartAsync(client, identity, destPath);
Defensive patterns

Strategy: retry

Try / catch

try { await UpdaterDownloadEngine.DownloadInstallerPartAsync(client, identity, destPath); }
catch (InvalidDataException ex) when (ex.Message.Contains("changed validator"))
{ UpdaterDownloadEngine.DeletePartialDownload(destPath); /* the remote artifact changed; re-fetch identity */ }

Prevention

When it happens

Trigger: A partial download was paused. On resume, the server returns 206 but with a different ETag or Last-Modified than recorded in the .part.json metadata, indicating the released artifact changed. The engine restarts, and on the fresh request the validator has changed yet again (e.g. a rolling deployment is actively replacing the asset), triggering the throw.

Common situations: The update server republished or overwrote the installer at the same URL with different content (new hash, new ETag). A CI/CD pipeline is mid-deploy and the asset is in flux. A mirror is serving a stale cached ETag that flips between two values. The release was yanked and replaced between the initial download and the resume.

Related errors


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