dutchcoders/transfer.sh · error
Could not crypt file
Error message
Could not crypt file
What it means
postHandler calls attachEncryptionReader(file, r.Header.Get("X-Encrypt-Password")) to wrap the file in an AES-GCM encryption reader when the X-Encrypt-Password header is present. If deriving the key or initializing the cipher fails, the server responds with 500 and this message. The upload is aborted before anything is written to the storage backend for the file body.
Source
Thrown at server/handlers.go:536
buffer := &bytes.Buffer{}
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Could not encode metadata", http.StatusInternalServerError)
return
} else if err := s.storage.Put(r.Context(), token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Could not save metadata", http.StatusInternalServerError)
return
}
s.logger.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
reader, err := attachEncryptionReader(file, r.Header.Get("X-Encrypt-Password"))
if err != nil {
http.Error(w, "Could not crypt file", http.StatusInternalServerError)
return
}
if err = s.storage.Put(r.Context(), token, filename, reader, contentType, uint64(contentLength)); err != nil {
s.logger.Printf("Backend storage error: %s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
filename = url.PathEscape(filename)
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename))
deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken))
w.Header().Add("X-Url-Delete", resolveURL(r, deleteURL, s.proxyPort))
responseBody += fmt.Sprintln(getURL(r, s.proxyPort).ResolveReference(relativeURL).String())
}
}
_, err := w.Write([]byte(responseBody))View on GitHub (pinned to c37bfd9579)
Solutions
- Ensure the X-Encrypt-Password header carries a non-empty, reasonable-length password.
- Omit the header entirely if you do not want encryption instead of sending it empty.
- Use the matching client library/version whose key derivation agrees with the server.
- Retry with a corrected header; no partial file was stored (though the .metadata object may already exist).
Example fix
// before: header present but empty
req.Header.Set("X-Encrypt-Password", "")
// after: send a real password or omit the header
req.Header.Set("X-Encrypt-Password", "correct horse battery staple") Defensive patterns
Strategy: validation
Validate before calling
// Ensure the encryption header is well-formed before sending
func encryptionHeaderValid(password string) bool {
return password != "" && len(password) >= 8
}
// only set header when valid
if encryptionHeaderValid(pw) {
req.Header.Set("X-Encrypt-Password", pw)
} Try / catch
resp, err := http.Post(url, mime, body)
if err == nil && resp.StatusCode == http.StatusInternalServerError &&
strings.Contains(readBody(resp), "Could not crypt file") {
return fmt.Errorf("encryption handshake failed; check X-Encrypt-Password header")
} Prevention
- Only send X-Encrypt-Password with a non-empty password; omit it otherwise.
- Use the matching official client so key derivation agrees with the server.
- Keep server and client versions in sync for encryption features.
- Remember the download side needs the same password to decrypt.
When it happens
Trigger: A request sets the X-Encrypt-Password header and attachEncryptionReader fails while initializing AES-GCM (key/cipher setup error). Empty or pathological header values combined with code changes to key derivation can also trigger it.
Common situations: Clients using encryption incorrectly with a modified or older server version where password handling differs; programmatic clients sending an empty X-Encrypt-Password header value (present but empty) and hitting edge cases in key setup.
Related errors
AI-assisted analysis of dutchcoders/transfer.sh@c37bfd9579 (2026-09-05).
Data as JSON: /api/errors/821af35a1764b37a.
Report an issue: GitHub.