sipeed/picoclaw · error

decrypt matrix media: %w

Error message

decrypt matrix media: %w

What it means

Thrown by MatrixChannel.downloadMedia when an inbound attachment is encrypted (msgEvt.File set, msgEvt.URL empty) and msgEvt.File.PrepareForDecryption fails (pkg/channels/matrix/matrix.go:1026). PrepareForDecryption parses the EncryptedFile JWK — key, iv, v, hashes.sha256 — and rejects malformed metadata: wrong key/IV lengths, missing hashes, or an unparseable JWK. The HTTP download already succeeded at this point; only client-side AES-CTR/Olm-style attachment decryption setup failed.

Source

Thrown at pkg/channels/matrix/matrix.go:1026

	if ctx != nil {
		dlCtx = ctx
	}
	reqCtx, cancel := context.WithTimeout(dlCtx, 20*time.Second)
	defer cancel()

	resp, err := c.client.Download(reqCtx, parsed)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	reader := resp.Body
	readerClose := func() error { return nil }

	// Encrypted attachments put URL in msgEvt.File and require client-side decryption.
	if msgEvt != nil && msgEvt.File != nil && msgEvt.URL == "" {
		if err = msgEvt.File.PrepareForDecryption(); err != nil {
			return "", fmt.Errorf("decrypt matrix media: %w", err)
		}
		decryptReader := msgEvt.File.DecryptStream(resp.Body)
		reader = decryptReader
		readerClose = decryptReader.Close
	}

	label := matrixMediaLabel(msgEvt, mediaKind)
	ext := matrixMediaExt(label, matrixContentType(msgEvt), mediaKind)
	mediaDir, err := matrixMediaTempDir()
	if err != nil {
		return "", fmt.Errorf("create matrix media directory: %w", err)
	}
	tmp, err := os.CreateTemp(mediaDir, "matrix-media-*"+ext)
	if err != nil {
		return "", err
	}
	tmpPath := tmp.Name()
	cleanup := true

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Inspect the event's file JSON: verify v=='v1', url is mxc, key.kty/alg/op/extractable shape, iv base64 16 bytes, hashes.sha256 present
  2. If metadata is malformed, skip the attachment and notify — retrying cannot fix remote content
  3. Confirm senders use spec-compliant attachment encryption (Matrix v1.1+ 'm.megolm-backup' era EncryptedFile shape)
  4. Check the bot can decrypt this room's events at all (crypto helper initialized, room keys received) — if the Olm layer is broken, plain files still work while encrypted ones fail elsewhere

Example fix

// before
 if err = msgEvt.File.PrepareForDecryption(); err != nil {
 	return "", fmt.Errorf("decrypt matrix media: %w", err)
 }

// after: skip malformed encrypted attachments without failing the handler
 if err = msgEvt.File.PrepareForDecryption(); err != nil {
 	logger.WarnCF("matrix", "undecryptable attachment", map[string]any{"error": err.Error()})
 	return "", &SkipAttachmentError{Cause: err} // caller notifies user, continues processing text
Defensive patterns

Strategy: fallback

Validate before calling

// structural pre-check of the encrypted-file JWK before attempting decryption
func encryptedFileWellFormed(f *event.EncryptedFile) bool {
	return f != nil && f.Key != nil && len(f.Key.K) > 0 &&
		len(f.IV) > 0 && f.Hashes != nil && f.Hashes.SHA256 != ""
}

Try / catch

path, err := c.downloadMedia(ctx, msgEvt, mediaKind)
if err != nil {
	var decErr *EncryptedMediaError // wrap downloadMedia's result in a typed error at your boundary
	if errors.As(err, &decErr) {
		notifyUser("attachment could not be decrypted; asking sender to re-send") // degrade, keep flow alive
		return nil, nil
	}
	return nil, err
}

Prevention

When it happens

Trigger: An m.room.encrypted media event whose file object lacks hashes or has a truncated key/iv; bridges or custom clients writing non-spec encrypted attachment metadata; event content mangled by a relay; a msgEvt.File built without URL inside, causing downstream decrypt errors after PrepareForDecryption.

Common situations: Receiving encrypted media from non-Element clients with divergent attachment encryption; bridge software that forwards file metadata incompletely; adversarial/malformed events; older spec drafts (v field missing) from ancient clients.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/908301e7cd1b6825. Report an issue: GitHub.