subhra74/xdm · error · NonRetriableException

NonResumable

NonResumable

Error message

Resume not supported

What it means

Thrown in PieceGrabber.Connect when a piece's total length is unknown (-1) and the request is not the first request for that stream type. The server did not provide a content length, so the downloader cannot resume into a partial stream and throws NonRetriableException with ErrorCode.NonResumable. This failure is deliberately not retried.

Solutions

  1. Restart the download from scratch (single connection, no resume) instead of resuming the piece.
  2. Verify the server supports Content-Length and Range requests; fall back to single-source download if not.
  3. Ensure IsFirstRequest/stream bookkeeping is correct so length-known first requests are not mistaken for resumes.

Example fix

// before
var piece = callback.GetPiece(pieceId); // Length == -1 on resume -> NonResumable
// after
if (piece.Length == -1) {
    // restart download without resume
    RestartDownload(disableResume: true);
}
Defensive patterns

Strategy: fallback

Validate before calling

var req = new HttpRequestMessage(HttpMethod.Head, url);
var resp = await client.SendAsync(req);
bool canResume = resp.Headers.AcceptRanges.Contains("bytes") && resp.Content.Headers.ContentLength.HasValue;
if (!canResume) disableResume = true;

Type guard

bool IsResumable(long pieceLength) => pieceLength != -1;

Try / catch

try { grabber.Connect(); }
catch (DownloadException e) when (e.ErrorCode == ErrorCode.NonResumable) {
    RestartDownloadWithoutResume();
}

Prevention

When it happens

Trigger: Calling Connect on a non-first request where callback.GetPiece(...).Length == -1, i.e. a resume attempt against a response with no usable length (chunked transfer or missing Content-Length) for that stream type.

Common situations: Servers that stream with Transfer-Encoding: chunked, dynamic responses (no Content-Length), resuming a download whose original headers no longer advertise a length, misconfigured proxy stripping Content-Length.

Related errors


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

Appendix: source

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

            this.sleepHandle.Set();
            this.sleepHandle.Close();
            this.pieceId = null;
            this.callback = null;
            try { this.fileWriterStream?.Dispose(); } catch { }
        }

        private HttpResponse Connect()
        {
            HttpResponse? response = null;
            var error = true;
            try
            {
                if (this.callback == null || this.pieceId == null) throw new OperationCanceledException();
                var piece = this.callback.GetPiece(this.pieceId);
                var firstRequest = this.callback.IsFirstRequest(piece.StreamType);

                if (piece.Length == -1 && !this.callback.IsFirstRequest(piece.StreamType))
                    throw new NonRetriableException(ErrorCode.NonResumable, "Resume not supported");

                var hc = this.callback.GetSharedHttpClient(this.pieceId);
                if (hc == null) throw new OperationCanceledException();
                request = CreateRequest(hc, piece);
                response = hc.Send(request);
                CancellationToken.ThrowIfCancellationRequested();
                response.EnsureSuccessStatusCode();

                var status = response.StatusCode;
                var contentLength = response.ContentLength;
                if (response.Compressed)
                {
                    contentLength = -1;
                    if (response.StatusCode == HttpStatusCode.PartialContent)
                    {
                        status = HttpStatusCode.OK;
                    }
                }

View on GitHub (pinned to 1ca5a25aae)