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

  1. Ensure the X-Encrypt-Password header carries a non-empty, reasonable-length password.
  2. Omit the header entirely if you do not want encryption instead of sending it empty.
  3. Use the matching client library/version whose key derivation agrees with the server.
  4. 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

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.