{"record":{"id":"d4a11720d517a02b","repo":"sipeed/picoclaw","slug":"empty-matrix-media-url","errorCode":null,"errorMessage":"empty matrix media URL","messagePattern":"empty matrix media URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/matrix/matrix.go","lineNumber":1000,"sourceCode":"\t\tif err == nil {\n\t\t\treturn ref\n\t\t}\n\t\tlogger.WarnCF(\"matrix\", \"Failed to store media in MediaStore, falling back to local path\", map[string]any{\n\t\t\t\"path\":  localPath,\n\t\t\t\"error\": err.Error(),\n\t\t})\n\t}\n\treturn localPath\n}\n\nfunc (c *MatrixChannel) downloadMedia(\n\tctx context.Context,\n\tmsgEvt *event.MessageEventContent,\n\tmediaKind string,\n) (string, error) {\n\turi := matrixMediaURI(msgEvt)\n\tif uri == \"\" {\n\t\treturn \"\", fmt.Errorf(\"empty matrix media URL\")\n\t}\n\tparsed := uri.ParseOrIgnore()\n\tif parsed.IsEmpty() {\n\t\treturn \"\", fmt.Errorf(\"invalid matrix media URL: %s\", uri)\n\t}\n\n\tdlCtx := c.baseContext()\n\tif ctx != nil {\n\t\tdlCtx = ctx\n\t}\n\treqCtx, cancel := context.WithTimeout(dlCtx, 20*time.Second)\n\tdefer cancel()\n\n\tresp, err := c.client.Download(reqCtx, parsed)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()","sourceCodeStart":982,"sourceCodeEnd":1018,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/matrix/matrix.go#L982-L1018","documentation":"Thrown by MatrixChannel.downloadMedia when matrixMediaURI(msgEvt) returns an empty string, i.e. the inbound Matrix event content carries neither URL nor File (pkg/channels/matrix/matrix.go:1000). For encrypted media the URI lives in msgEvt.File.URL; for plain media in msgEvt.URL. The error means the event was routed into the media-download path although its content has no media reference at all.","triggerScenarios":"Dispatching a non-media event (m.text, m.notice, location, or a media type matrixMediaURI does not cover) into downloadMedia; event content where both url and file fields are absent (some stickers/voice variants or custom appservice events); msgEvt being zero-valued due to a decoding step that dropped fields.","commonSituations":"Extending the inbound dispatcher to new message types without updating the media-URI extractor; bridges/appservices emitting non-standard content shapes; schema/version drift where MsgType checks pass but URL/File fields moved.","solutions":["Gate downloadMedia on matrixMediaURI(msgEvt) != \"\" (or msgEvt.URL != \"\" || msgEvt.File != nil) at the dispatch site","Check the event's actual MsgType/content JSON in client debug logs to see why no media field is present","Extend matrixMediaURI if the variant legitimately stores the URI elsewhere (e.g. File.URL for encrypted, or new fields)","Skip the attachment gracefully rather than failing the whole inbound message"],"exampleFix":"// before\n if isMediaMsg(msgEvt) {\n \tpath, err := c.downloadMedia(ctx, msgEvt, kind)\n }\n\n// after\n if isMediaMsg(msgEvt) && matrixMediaURI(msgEvt) != \"\" {\n \tpath, err := c.downloadMedia(ctx, msgEvt, kind)\n } else {\n \tlogger.DebugC(\"matrix\", \"media event without mxc URI; skipping download\")\n }","handlingStrategy":"type-guard","validationCode":"// dispatch-site guard\nif msgEvt.URL == \"\" && (msgEvt.File == nil || msgEvt.File.URL == \"\") {\n\treturn nil // event carries no media reference: skip download path\n}\npath, err := c.downloadMedia(ctx, msgEvt, mediaKind)","typeGuard":"func hasMatrixMediaURI(msgEvt *event.MessageEventContent) bool {\n\tif msgEvt == nil { return false }\n\tif msgEvt.URL != \"\" { return true }\n\treturn msgEvt.File != nil && msgEvt.File.URL != \"\"\n}","tryCatchPattern":"path, err := c.downloadMedia(ctx, msgEvt, mediaKind)\nif err != nil {\n\tif strings.Contains(err.Error(), \"empty matrix media URL\") {\n\t\treturn nil, nil // not actually media: drop attachment, keep processing text\n\t}\n\treturn nil, err\n}","preventionTips":["Route to media download only on MsgType m.image/m.video/m.audio/m.file AND a non-empty URI","Keep matrixMediaURI coverage in sync when supporting new event shapes (stickers, encrypted files)","Treat missing-URI events as skip, not error, so one bad event cannot stall sync processing"],"tags":["go","matrix","media","validation","inbound","mxc-uri"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}