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
- Use the exact X-Url-Delete value returned when the file was uploaded.
- Ensure the header is sent correctly (quote it in the shell; confirm it is not stripped by proxies/CDNs).
- If the token is lost, the file can only be removed by storage administrators directly from the backend or by waiting for expiry/purge.
- 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
- Record the X-Url-Delete response header at upload time in a secure store.
- Never confuse the URL token with the deletion token — they are different secrets.
- Quote the header value in shell commands to avoid whitespace stripping.
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
- maxDownloads expired
- maxDate expired
- metadata doesn't exist
- cannot find file %s/%s
- Could not delete file.
AI-assisted analysis of dutchcoders/transfer.sh@c37bfd9579 (2026-09-05).
Data as JSON: /api/errors/f2ae2377e5e98ff8.
Report an issue: GitHub.