{"record":{"id":"5ba3eb8d7eb0f676","repo":"sipeed/picoclaw","slug":"matrix-send-media-w","errorCode":null,"errorMessage":"matrix send media: %w","messagePattern":"matrix send media: %w","errorType":"exception","errorClass":"channels.ErrTemporary","httpStatus":null,"severity":"error","filePath":"pkg/channels/matrix/matrix.go","lineNumber":565,"sourceCode":"\n\t\tmsgType := matrixOutboundMsgType(part.Type, filename, contentType)\n\t\tcontent := matrixOutboundContent(\n\t\t\tpart.Caption,\n\t\t\tfilename,\n\t\t\tmsgType,\n\t\t\tcontentType,\n\t\t\tfileInfo.Size(),\n\t\t\tuploadResp.ContentURI.CUString(),\n\t\t)\n\n\t\tsendResp, err := c.client.SendMessageEvent(sendCtx, roomID, event.EventMessage, content)\n\t\tif err != nil {\n\t\t\tlogger.ErrorCF(\"matrix\", \"Failed to send media message\", map[string]any{\n\t\t\t\t\"room_id\": roomID.String(),\n\t\t\t\t\"type\":    msgType,\n\t\t\t\t\"error\":   err.Error(),\n\t\t\t})\n\t\t\treturn nil, fmt.Errorf(\"matrix send media: %w\", channels.ErrTemporary)\n\t\t}\n\t\tif sendResp != nil {\n\t\t\teventIDs = append(eventIDs, sendResp.EventID.String())\n\t\t}\n\t}\n\n\tif hasTrackedMsg {\n\t\tc.dismissTrackedToolFeedbackMessage(ctx, msg.ChatID, trackedMsgID)\n\t}\n\n\treturn eventIDs, nil\n}\n\n// StartTyping implements channels.TypingCapable.\nfunc (c *MatrixChannel) StartTyping(ctx context.Context, chatID string) (func(), error) {\n\tif !c.IsRunning() {\n\t\treturn func() {}, nil\n\t}","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/matrix/matrix.go#L547-L583","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := matrixCh.SendMedia(ctx, msg); err != nil {\n\tif errors.Is(err, channels.ErrTemporary) {\n\t\t// read the logged 'Failed to send media message' entry: 403 membership errors need re-invite, not retry\n\t\tif lastSendErrWasForbidden() { suspendRoom(msg.ChatID); return nil }\n\t\treturn err // 429/5xx: manager backoff handles it\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["go","matrix","media","send","network","rate-limit","retry","temporary-failure"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}