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
- Read the logged error for room_id/type to distinguish 429 (wait/backoff) from 403 (membership)
- If 403: re-invite/join the bot, or resolve the room alias/upgrade successor and re-target the send
- If 429: pace media sends and rely on the manager's exponential backoff
- 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
- Watch send-vs-upload failures separately in logs; only sends fail after uploads succeed
- Verify bot room membership before large media campaigns
- Resolve room upgrades (tombstones) so sends target the current room ID
- Deduplicate on the media content when retrying to avoid double posts
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
- matrix send: %w
- matrix upload media: %w
- API error %d: %s
- after %d retries: %w
- LLM call failed after retries: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/5ba3eb8d7eb0f676.
Report an issue: GitHub.