dutchcoders/transfer.sh · warning
maxDownloads expired
Error message
maxDownloads expired
What it means
checkMetadata validates a file's stored .metadata JSON before serving it. Each upload carries an optional MaxDownloads limit; when the recorded Downloads count has already reached MaxDownloads (MaxDownloads != -1), the server refuses to serve the file and returns this error, then typically purges the file. It is the per-file download quota being exhausted.
Source
Thrown at server/handlers.go:886
}
func (s *Server) checkMetadata(ctx context.Context, token, filename string, increaseDownload bool) (metadata, error) {
s.lock(token, filename)
defer s.unlock(token, filename)
var metadata metadata
r, _, err := s.storage.Get(ctx, token, fmt.Sprintf("%s.metadata", filename), nil)
defer storage.CloseCheck(r)
if err != nil {
return metadata, err
}
if err := json.NewDecoder(r).Decode(&metadata); err != nil {
return metadata, err
} else if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads {
return metadata, errors.New("maxDownloads expired")
} else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) {
return metadata, errors.New("maxDate expired")
} else if metadata.MaxDownloads != -1 && increaseDownload {
// todo(nl5887): mutex?
// update number of downloads
metadata.Downloads++
buffer := &bytes.Buffer{}
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
return metadata, errors.New("could not encode metadata")
} else if err := s.storage.Put(ctx, token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
return metadata, errors.New("could not save metadata")
}
}
return metadata, nil
}View on GitHub (pinned to c37bfd9579)
Solutions
- Ask the sender to re-upload and share a fresh link (the expired file is consumed by design).
- Re-upload with a higher MaxDownloads value (or omit the limit) if repeated access is expected.
- Disable prefetching/AV scanning on the download client so it does not burn the quota before the real download.
- If this is your own deployment and behavior is undesired, adjust or remove the MaxDownloads enforcement in server/handlers.go checkMetadata.
Example fix
// before (upload with 1 download allowed) curl --upload-file ./f -H 'Max-Downloads: 1' https://host/f // after (unlimited downloads) curl --upload-file ./f -H 'Max-Downloads: -1' https://host/f
Defensive patterns
Strategy: try-catch
Try / catch
resp, err := http.Get(url)
if err == nil {
body, _ := io.ReadAll(resp.Body)
if strings.Contains(string(body), "maxDownloads expired") {
// request a fresh link from the sender
}
} Prevention
- Set Max-Downloads generously (or -1) when the link may be fetched by scanners or download managers.
- Never preview/prefetch one-time links; one fetch consumes the quota.
- Communicate the link's single-use nature to recipients up front.
When it happens
Trigger: GET/HEAD/preview/zip/tar/tar.gz download of a token whose metadata has MaxDownloads >= 1 and Downloads >= MaxDownloads, i.e. the file was configured with a limited number of downloads and all of them have been consumed.
Common situations: Sender shared a one-time (X-Url-Delete style, MaxDownloads=1) link and the recipient retries after the first fetch; a download manager or preview bot fetching the URL multiple times exhausts the quota before the human opens it; load balancer health checks hitting the URL.
Related errors
- maxDate expired
- could not encode metadata
- could not save metadata
- metadata doesn't exist
- deletion token doesn't match
AI-assisted analysis of dutchcoders/transfer.sh@c37bfd9579 (2026-09-05).
Data as JSON: /api/errors/bbda76958c6ee1c6.
Report an issue: GitHub.