dutchcoders/transfer.sh · warning

deletion token doesn't match

Error message

deletion token doesn't match

What it means

After successfully loading metadata, checkDeletionToken compares metadata.DeletionToken with the deletionToken supplied by the caller. On mismatch it returns this error and refuses to delete the file. It is an authorization failure: the delete request did not present the correct secret token recorded at upload time.

Source

Thrown at server/handlers.go:924

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 {
		return err
	}

	if err := json.NewDecoder(r).Decode(&metadata); err != nil {
		return err
	} else if metadata.DeletionToken != deletionToken {
		return errors.New("deletion token doesn't match")
	}

	return nil
}

func (s *Server) purgeHandler() {
	ticker := time.NewTicker(s.purgeInterval)
	go func() {
		for {
			<-ticker.C
			err := s.storage.Purge(context.TODO(), s.purgeDays)
			if err != nil {
				s.logger.Printf("error cleaning up expired files: %v", err)
			}
		}
	}()
}

View on GitHub (pinned to c37bfd9579)

Solutions

  1. Use the exact X-Url-Delete value returned when the file was uploaded.
  2. Ensure the header is sent correctly (quote it in the shell; confirm it is not stripped by proxies/CDNs).
  3. If the token is lost, the file can only be removed by storage administrators directly from the backend or by waiting for expiry/purge.
  4. Re-upload to obtain a fresh file plus deletion token if the original cannot be recovered.

Example fix

// before
curl -X DELETE https://host/token/f.txt -H 'X-Url-Delete: guessed'
// after
curl -X DELETE https://host/token/f.txt -H 'X-Url-Delete: <token-printed-at-upload>'
Defensive patterns

Strategy: validation

Validate before calling

// only send DELETE when a deletion token is present and non-empty
if deletionToken == "" {
	return errors.New("refusing to delete: deletion token unknown")
}

Type guard

func hasDeletionToken(tok string) bool { return len(tok) > 0 }

Try / catch

resp, err := doDelete(token, file, delToken)
if err == nil && resp.StatusCode != http.StatusOK {
	b, _ := io.ReadAll(resp.Body)
	if strings.Contains(string(b), "deletion token doesn't match") {
		// stop retrying with wrong credentials; recover token or abandon
	}
}

Prevention

When it happens

Trigger: DELETE request whose X-Url-Delete header (or URL-embedded deletion token) does not equal the DeletionToken stored in the file's .metadata, including missing header, empty string, or a token belonging to a different file.

Common situations: Losing the original deletion token returned in the X-Url-Delete response header at upload time; copying the URL token instead of the deletion token; shell stripping/altering the header value; attempting to delete someone else's upload.

Related errors


AI-assisted analysis of dutchcoders/transfer.sh@c37bfd9579 (2026-09-05). Data as JSON: /api/errors/f2ae2377e5e98ff8. Report an issue: GitHub.