sipeed/picoclaw · error · channels.ErrTemporary

matrix send media: %w

Error message

matrix send media: %w

What it means

Thrown by MatrixChannel.SendMedia when the room message event carrying the uploaded media (m.image/m.video/m.file/m.audio via SendMessageEvent) fails after a successful upload (pkg/channels/matrix/matrix.go:565). Wraps channels.ErrTemporary (manager retries with backoff) and logs room_id, type, and the underlying error. The upload itself succeeded, so retries re-upload the bytes unless the caller caches the mxc URI.

Source

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

		msgType := matrixOutboundMsgType(part.Type, filename, contentType)
		content := matrixOutboundContent(
			part.Caption,
			filename,
			msgType,
			contentType,
			fileInfo.Size(),
			uploadResp.ContentURI.CUString(),
		)

		sendResp, err := c.client.SendMessageEvent(sendCtx, roomID, event.EventMessage, content)
		if err != nil {
			logger.ErrorCF("matrix", "Failed to send media message", map[string]any{
				"room_id": roomID.String(),
				"type":    msgType,
				"error":   err.Error(),
			})
			return nil, fmt.Errorf("matrix send media: %w", channels.ErrTemporary)
		}
		if sendResp != nil {
			eventIDs = append(eventIDs, sendResp.EventID.String())
		}
	}

	if hasTrackedMsg {
		c.dismissTrackedToolFeedbackMessage(ctx, msg.ChatID, trackedMsgID)
	}

	return eventIDs, nil
}

// StartTyping implements channels.TypingCapable.
func (c *MatrixChannel) StartTyping(ctx context.Context, chatID string) (func(), error) {
	if !c.IsRunning() {
		return func() {}, nil
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Read the logged error for room_id/type to distinguish 429 (wait/backoff) from 403 (membership)
  2. If 403: re-invite/join the bot, or resolve the room alias/upgrade successor and re-target the send
  3. If 429: pace media sends and rely on the manager's exponential backoff
  4. For frequently failing networks, deduplicate retries by content URI when your integration caches uploads
Defensive patterns

Strategy: retry

Try / catch

if _, err := matrixCh.SendMedia(ctx, msg); err != nil {
	if errors.Is(err, channels.ErrTemporary) {
		// read the logged 'Failed to send media message' entry: 403 membership errors need re-invite, not retry
		if lastSendErrWasForbidden() { suspendRoom(msg.ChatID); return nil }
		return err // 429/5xx: manager backoff handles it
	}
	return err
}

Prevention

When it happens

Trigger: Homeserver rate limiting the send call (429 M_LIMIT_EXCEEDED) right after a heavy upload; bot kicked out of the room between upload and send (403 M_FORBIDDEN); network drop after upload; room frozen/upgrade tombstone making the old room unsendable.

Common situations: Bursty media posting hitting per-room/per-user limits; room moderation actions while media jobs were queued; flaky networks where the large upload drains the connection window; sending to an upgraded room's old ID.

Related errors


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