AlistGo/alist · error
can't create RangeReadCloser since URL is empty in link
Error message
can't create RangeReadCloser since URL is empty in link
What it means
GetRangeReadCloserFromLink requires a URL-based link to construct a ranged HTTP reader. The provided model.Link has an empty URL (typically because the driver returned a Link carrying only a Data io.ReadCloser, e.g. for local or proxy-style drivers), so no RangeReadCloser can be built.
Source
Thrown at internal/stream/util.go:19
package stream
import (
"context"
"encoding/hex"
"fmt"
"io"
"net/http"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/net"
"github.com/alist-org/alist/v3/pkg/http_range"
"github.com/alist-org/alist/v3/pkg/utils"
log "github.com/sirupsen/logrus"
)
func GetRangeReadCloserFromLink(size int64, link *model.Link) (model.RangeReadCloserIF, error) {
if len(link.URL) == 0 {
return nil, fmt.Errorf("can't create RangeReadCloser since URL is empty in link")
}
rangeReaderFunc := func(ctx context.Context, r http_range.Range) (io.ReadCloser, error) {
if link.Concurrency != 0 || link.PartSize != 0 {
header := net.ProcessHeader(nil, link.Header)
down := net.NewDownloader(func(d *net.Downloader) {
d.Concurrency = link.Concurrency
d.PartSize = link.PartSize
})
req := &net.HttpRequestParams{
URL: link.URL,
Range: r,
Size: size,
HeaderRef: header,
}
rc, err := down.Download(ctx, req)
return rc, err
}View on GitHub (pinned to 843d9dc814)
Solutions
- Use a driver/mode that returns a direct URL link (e.g. enable direct link / disable force proxy for that storage)
- Before calling, check len(link.URL) > 0 and fall back to a full-download/seekable-cache strategy
- Fix the driver's Link() implementation to populate URL when the backend supports ranged GETs
Example fix
// before
rrc, err := stream.GetRangeReadCloserFromLink(size, link) // error: URL is empty
// after
if len(link.URL) == 0 {
// fall back: buffer whole file to a temp file for random access
f, err := stream.CacheFullInTempFile(link.Data)
...
} Defensive patterns
Strategy: type-guard
Validate before calling
if len(link.URL) == 0 {
// take the full-download path instead of ranged reads
return downloadWhole(link.Data)
} Type guard
func hasURLLink(link *model.Link) bool {
return link != nil && len(link.URL) > 0
} Prevention
- Check link.URL before requesting range-based features (seek/preview)
- Prefer drivers/modes that expose direct URLs when range access is required
- Cache a seekable copy for Data-only links before random access
When it happens
Trigger: Calling a code path that needs random access (preview, video seek, archive listing) on a file whose driver Link exposes Data instead of URL, or the URL field was never populated before the call.
Common situations: Using a driver that only supports full-stream proxying with a feature that requires range requests; upstream changes where a driver stopped returning direct URLs.
Related errors
- http request failure, err:%s
- not get file info
- chunkNum invalid
- invalid label ID '%s': %v
- invalid label_id '%s': %v
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/bb0434f927cc8b55.
Report an issue: GitHub.