{"record":{"id":"22462af1628abc88","repo":"sipeed/picoclaw","slug":"invalid-matrix-media-url-s","errorCode":null,"errorMessage":"invalid matrix media URL: %s","messagePattern":"invalid matrix media URL: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/matrix/matrix.go","lineNumber":1004,"sourceCode":"\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()\n\n\treader := resp.Body\n\treaderClose := func() error { return nil }\n","sourceCodeStart":986,"sourceCodeEnd":1022,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/matrix/matrix.go#L986-L1022","documentation":"Thrown by MatrixChannel.downloadMedia when the media URI string exists but id.ContentURI.ParseOrIgnore yields an empty ContentURI — the value is not a valid mxc://server/mediaId reference (pkg/channels/matrix/matrix.go:1004). The offending raw URI is included in the message. Matrix content repositories address media exclusively by mxc:// URIs; anything else (http links, truncated strings, custom schemes) cannot be downloaded via client.Download.","triggerScenarios":"A homeserver or bridge populating the url field with an https:// link instead of mxc:// (some bridges do this for avatars/files); a truncated or hand-crafted event with garbage in url; content rewritten by a non-compliant client; mxc URIs with empty media ID or missing server part.","commonSituations":"Bridging from Slack/Discord/Telegram via software that inlines external URLs; events from defederated/custom servers with unusual URI formats; fuzzed or adversarial event content reaching the bot.","solutions":["Log the raw URI from the message and inspect the originating event JSON to see who wrote a non-mxc value","If http(s) URLs are expected from a bridge, fetch them out-of-band with a normal HTTP client instead of client.Download","Skip the attachment with a user-visible notice rather than failing message processing","For federated media, ensure the server part of the mxc URI resolves (correct server name)"],"exampleFix":"// before\n resp, err := c.client.Download(reqCtx, parsed) // parsed empty, fails oddly\n\n// after: gate on a strict mxc check before download\n if uri := matrixMediaURI(msgEvt); uri != \"\" {\n \tparsed := uri.ParseOrIgnore()\n \tif parsed.IsEmpty() {\n \t\tlogger.WarnCF(\"matrix\", \"skipping non-mxc media URI\", map[string]any{\"uri\": string(uri)})\n \t\treturn \"\", nil // or a typed skip error\n \t}\n }","handlingStrategy":"type-guard","validationCode":"// strict mxc check before download\nvar mxcPattern = regexp.MustCompile(`^mxc://[^/]+/[^/]+$`)\nuri := matrixMediaURI(msgEvt)\nif !mxcPattern.MatchString(string(uri)) {\n\treturn nil, &SkipAttachmentError{Reason: \"non-mxc media URI: \" + string(uri)}\n}","typeGuard":"func isUsableMxcURI(uri id.ContentURIString) bool {\n\tparsed := uri.ParseOrIgnore()\n\treturn !parsed.IsEmpty() && parsed.Homeserver != \"\" && parsed.FileID != \"\"\n}","tryCatchPattern":"path, err := c.downloadMedia(ctx, msgEvt, mediaKind)\nif err != nil {\n\tif strings.Contains(err.Error(), \"invalid matrix media URL\") {\n\t\tlog.Warn(\"skipping malformed media URI: \", err)\n\t\treturn nil, nil // untrusted input: skip, never retry\n\t}\n\treturn nil, err\n}","preventionTips":["Validate mxc URIs at the trust boundary — inbound event content is untrusted","Bridges emitting https URLs need a separate fetch path, not client.Download","Log and skip malformed URIs so adversarial events cannot DoS the media pipeline"],"tags":["go","matrix","media","mxc-uri","validation","inbound","bridging"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}