dutchcoders/transfer.sh · error
could not save metadata
Error message
could not save metadata
What it means
When incrementing the download counter, checkMetadata writes the updated metadata back to storage as <filename>.metadata via s.storage.Put. If the storage backend returns an error, the download is aborted with this error. It means the server could not persist the incremented download count even though the file itself exists.
Source
Thrown at server/handlers.go:899
}
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
}
func (s *Server) checkDeletionToken(ctx context.Context, deletionToken, token, filename string) 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 s.storage.IsNotExist(err) {
return errors.New("metadata doesn't exist")
} else if err != nil {View on GitHub (pinned to c37bfd9579)
Solutions
- Check the storage backend health and credentials (bucket access, tokens, disk space/permissions on basedir).
- Inspect server logs for the underlying storage error returned by Put to identify the backend-specific cause.
- Retry the download once the backend is reachable; the count increment failure is transient in outage cases.
- For local storage, verify the basedir is writable by the server process user; for S3 verify IAM permissions include PutObject.
Example fix
// before
} 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")
}
// after
} else if err := s.storage.Put(ctx, token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
return metadata, fmt.Errorf("could not save metadata: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
# check backend reachability before bulk downloads curl -sf http://storage-backend-health || echo "storage down"
Try / catch
if err != nil && strings.Contains(err.Error(), "could not save metadata") {
time.Sleep(backoff)
return retryDownload() // transient backend outage
} Prevention
- Monitor storage backend health and credentials in production.
- Ensure the local basedir is writable and has free disk space.
- For S3, verify IAM policy allows PutObject on the metadata objects.
When it happens
Trigger: GET/HEAD/preview/archive download of a file with a finite MaxDownloads where the storage Put call for the .metadata object fails — e.g. S3/GDrive/Storj/local backend outage, permission denied on the local basedir, bucket credentials revoked, or network failure to the backend.
Common situations: Local basedir made read-only or disk full; S3 bucket policy/credentials changed after uploads; Google Drive API quota or token expiry; storage backend temporarily unreachable mid-download.
Related errors
- metadata doesn't exist
- Could not save metadata
- maxDownloads expired
- maxDate expired
- could not encode metadata
AI-assisted analysis of dutchcoders/transfer.sh@c37bfd9579 (2026-09-05).
Data as JSON: /api/errors/92c80d5d85fb881a.
Report an issue: GitHub.