subhra74/xdm · error · DownloadException

InvalidResponse

InvalidResponse

Error message

Invalid response: {e.Message}

What it means

PieceGrabber.Download2 catches HttpException thrown by the HTTP response validation and, when the status code is a defined HttpStatusCode, rethrows it as a DownloadException with ErrorCode.InvalidResponse and message 'Invalid response: <original message>'. This converts raw HTTP failures during piece download into a typed download error.

Solutions

  1. Check the wrapped HttpException's StatusCode: 403/401 means re-fetch fresh URL/cookies; 416 means reset the resume offset and restart the piece
  2. Retry the piece download with fresh headers; signed CDN URLs frequently expire between segments
  3. For persistent 4xx/5xx, restart the whole download so PieceGrabber re-resolves the source URL
  4. Verify the server supports byte-range requests (Accept-Ranges); fall back to single-connection download if not

Example fix

// before
catch (HttpException e)
{
    var status = e.StatusCode;
    if (Enum.IsDefined(typeof(HttpStatusCode), status))
        throw new DownloadException(ErrorCode.InvalidResponse, "Invalid response: " + e.Message, e);
}
// after
catch (HttpException e)
{
    var status = e.StatusCode;
    if (Enum.IsDefined(typeof(HttpStatusCode), status))
    {
        if ((int)status == 403 || (int)status == 401) RefreshSourceUrlAndCookies();
        if ((int)status == 416) ResetPieceOffsets();
        throw new DownloadException(ErrorCode.InvalidResponse,
            $"Invalid response ({(int)status}): {e.Message}", e);
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check the piece URL and range support before Download2
var req = (HttpWebRequest)WebRequest.Create(pieceUrl);
req.Method = "HEAD";
using (var resp = (HttpWebResponse)await req.GetResponseAsync())
{
    bool supportsRanges = resp.Headers[HttpResponseHeader.AcceptRanges] == "bytes";
    if (!supportsRanges) FallBackToSingleConnectionDownload();
}

Try / catch

try
{
    grabber.Download2(...);
}
catch (DownloadException de) when (de.ErrorCode == ErrorCode.InvalidResponse)
{
    var http = (HttpException)de.InnerException;
    if ((int)http.StatusCode == 403 || (int)http.StatusCode == 401)
        RefreshUrlAndCookiesThenRetry();
    else if ((int)http.StatusCode == 416)
        ResetResumeOffsetsThenRetry();
    else
        throw;
}

Prevention

When it happens

Trigger: Download2 requests a byte-range piece and the server responds with a non-success status (via EnsureSuccessStatusCode), whose message is wrapped into DownloadException(ErrorCode.InvalidResponse).

Common situations: Server returns 403/404 for range requests because the URL or signed token expired mid-download; 416 Range Not Satisfiable on bad resume offsets; 5xx from overloaded servers; CDN dropping support for the requested range.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of subhra74/xdm@1ca5a25aae (2026-09-13). Data as JSON: /api/errors/4eaab3df07c92c76. Report an issue: GitHub.

Appendix: source

Thrown at app/XDM/XDM.Core/Downloader/Progressive/PieceGrabber.cs:103

                            }
                            connectPhase = false;
                            this.Download(response);
                        }

                        OnComplete();
                        return;
                    }
                    catch (TextRedirectException e)
                    {
                        this.redirectUri = e.RedirectUri;
                        continue;
                    }
                    catch (HttpException e)
                    {
                        var status = e.StatusCode;
                        if (Enum.IsDefined(typeof(HttpStatusCode), status))
                        {
                            throw new DownloadException(ErrorCode.InvalidResponse,
                                "Invalid response: " + e.Message, e);
                        }
                    }
                    catch (Exception e)
                    {
                        if (e is KeyNotFoundException || this.CancellationToken.IsCancellationRequested) return;
                        if (e is AssembleFailedException || e is NonRetriableException || e is OperationCanceledException) throw;
                        Log.Debug(e, "Error in PieceGrabber inner block - swallowing error - isCancelled: " + this.cancellationTokenSource.IsCancellationRequested);
                    }
                    timesRetried++;
                    if (timesRetried > Config.Instance.MaxRetry)
                    {
                        throw new DownloadException(ErrorCode.MaxRetryFailed, "Max retry exceeded");
                    }
                    if (connectPhase)
                    {
                        sleep(Config.Instance.RetryDelay * 1000);
                        CancellationToken.ThrowIfCancellationRequested();

View on GitHub (pinned to 1ca5a25aae)