chenhg5/cc-connect · error

matrix: send encrypted

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.

Solutions

  1. 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.
  2. Verify connectivity to the homeserver (curl /_matrix/client/versions) and retry the send.
  3. Ensure the room ID is correct and the bot account is a joined member of that room.
  4. Implement/raise retry with backoff for transient 5xx/network errors before surfacing to the user.
  5. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/df8ac563e5ef0069. Report an issue: GitHub.

Appendix: 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 enc

View on GitHub (pinned to 4000b2338a)