chenhg5/cc-connect · error
matrix: send encrypted: %w
Error message
matrix: send encrypted: %w
What it means
This error wraps a failure from client.SendMessageEvent when delivering an already-encrypted event (type m.room.encrypted) to the homeserver. The content encrypted fine, but the HTTP transaction to send it failed, so the message was not delivered.
Source
Thrown at platform/matrix/e2ee.go:243
// tryEncryptAndSend attempts to encrypt and send an event if E2EE is available.
// Returns (true, nil) if handled, (true, err) if handled with error, (false, nil) if not handled.
func (p *Platform) tryEncryptAndSend(ctx context.Context, client *mautrix.Client, roomID id.RoomID, evtType event.Type, content any) (bool, error) {
ch := p.getE2EECryptoHelper()
if ch == nil {
return false, nil
}
if !p.isRoomEncrypted(ctx, roomID) {
return false, nil
}
encContent, err := ch.Encrypt(ctx, roomID, evtType, content)
if err != nil {
return true, fmt.Errorf("matrix: encrypt: %w", err)
}
_, err = client.SendMessageEvent(ctx, roomID, event.EventEncrypted, encContent)
if err != nil {
return true, fmt.Errorf("matrix: send encrypted: %w", err)
}
return true, nil
}
func (p *Platform) isRoomEncrypted(ctx context.Context, roomID id.RoomID) bool {
client := p.getClient()
if client == nil || client.StateStore == nil {
return false
}
ss, ok := client.StateStore.(crypto.StateStore)
if !ok {
return false
}
enc, err := ss.IsEncrypted(ctx, roomID)
if err != nil {
return false
}
return encView on GitHub (pinned to 4000b2338a)
Solutions
- Check the wrapped cause for an HTTP status: 403 means join the room first (verify auto-join completed), 429 means back off and retry per Retry-After.
- Verify connectivity to the homeserver (curl /_matrix/client/versions) and retry the send.
- Ensure the room ID is correct and the bot account is a joined member of that room.
- Implement/raise retry with backoff for transient 5xx/network errors before surfacing to the user.
- Check homeserver logs for the corresponding request rejection if client-side details are insufficient.
Example fix
// before
_, err = client.SendMessageEvent(ctx, roomID, event.EventEncrypted, encContent)
if err != nil {
return true, fmt.Errorf("matrix: send encrypted: %w", err)
}
// after
_, err = client.SendMessageEvent(ctx, roomID, event.EventEncrypted, encContent)
if err != nil {
if errors.Is(err, mautrix.MForbidden) {
slog.Warn("matrix: not allowed to send, bot may not be joined", "room", roomID)
}
return true, fmt.Errorf("matrix: send encrypted: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
// Go: verify bot membership before attempting an encrypted send
members, err := client.JoinedMembers(ctx, roomID)
if err != nil || !containsUser(members, client.UserID) {
return fmt.Errorf("bot not joined to %s; skipping encrypted send", roomID)
} Try / catch
_, err = client.SendMessageEvent(ctx, roomID, event.EventEncrypted, encContent)
var httpErr mautrix.HTTPError
if errors.As(err, &httpErr) {
switch httpErr.Response.StatusCode {
case 403: // join room / fix permissions then retry
case 429: // honor Retry-After then retry
default: // surface with backoff
}
} Prevention
- Confirm auto-join completed before the first outbound message to a new room.
- Add exponential backoff for transient homeserver 5xx and network errors.
- Cap concurrent sends per room to avoid 429 rate limits.
- Alert on repeated SendMessageEvent failures as a homeserver health signal.
When it happens
Trigger: sendRoomEvent → tryEncryptAndSend: encryption succeeded, then client.SendMessageEvent(ctx, roomID, event.EventEncrypted, encContent) returns err — network failure, homeserver 4xx/5xx (e.g. 403 forbidden because the bot is not joined to the room, 429 rate limit), or request timeout.
Common situations: Bot invite not yet accepted so the homeserver rejects the send (M_FORBIDDEN); homeserver outage or restart mid-send; network partition between bridge and homeserver; hitting sync/send rate limits in a busy room.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- init crypto: %w
- too many redirects
- range chunk retries exhausted
- request usage endpoint: %w
- usage endpoint returned status %d: %s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/df8ac563e5ef0069.
Report an issue: GitHub.