{"record":{"id":"a86cf887bf4f69b1","repo":"sipeed/picoclaw","slug":"no-media-store-available-w-a86cf8","errorCode":null,"errorMessage":"no media store available: %w","messagePattern":"no media store available: %w","errorType":"exception","errorClass":"channels.ErrSendFailed","httpStatus":null,"severity":"error","filePath":"pkg/channels/matrix/matrix.go","lineNumber":474,"sourceCode":"func (c *MatrixChannel) SendMedia(ctx context.Context, msg bus.OutboundMediaMessage) ([]string, error) {\n\tif !c.IsRunning() {\n\t\treturn nil, channels.ErrNotRunning\n\t}\n\ttrackedMsgID, hasTrackedMsg := c.currentToolFeedbackMessage(msg.ChatID)\n\n\tsendCtx := ctx\n\tif sendCtx == nil {\n\t\tsendCtx = context.Background()\n\t}\n\n\troomID := id.RoomID(strings.TrimSpace(msg.ChatID))\n\tif roomID == \"\" {\n\t\treturn nil, fmt.Errorf(\"matrix room ID is empty: %w\", channels.ErrSendFailed)\n\t}\n\n\tstore := c.GetMediaStore()\n\tif store == nil {\n\t\treturn nil, fmt.Errorf(\"no media store available: %w\", channels.ErrSendFailed)\n\t}\n\n\tvar eventIDs []string\n\tfor _, part := range msg.Parts {\n\t\tif err := sendCtx.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tlocalPath, meta, err := store.ResolveWithMeta(part.Ref)\n\t\tif err != nil {\n\t\t\tlogger.ErrorCF(\"matrix\", \"Failed to resolve media ref\", map[string]any{\n\t\t\t\t\"ref\":   part.Ref,\n\t\t\t\t\"error\": err.Error(),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tfileInfo, err := os.Stat(localPath)","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/matrix/matrix.go#L456-L492","documentation":"Thrown by MatrixChannel.SendMedia when c.GetMediaStore() returns nil (pkg/channels/matrix/matrix.go:474). The media store is the component that resolves part.Ref to a local file plus metadata (ResolveWithMeta); without it no media can be uploaded. Wrapping is channels.ErrSendFailed — permanent, no retry. The error means the channel instance was constructed without a media store wired in, i.e. a build/configuration defect, not a runtime Matrix failure.","triggerScenarios":"Constructing MatrixChannel without the media-store option/dependency (framework wiring omitted); embedding the channel in a custom host app that never installs a media store; a nil store injected via empty constructor; calling SendMedia on a channel only intended for text.","commonSituations":"Custom integrations reusing MatrixChannel outside the standard bot bootstrap; refactors that made the media store optional and forgot one construction path; tests instantiating the channel minimally then exercising media sends.","solutions":["Find where MatrixChannel is built and wire the media store option/field before start","Check capability instead of guessing: if GetMediaStore() is nil after construction, fail fast at startup rather than on first media send","If media is intentionally unsupported, block media outbound messages upstream so SendMedia is never called"],"exampleFix":"// before\n ch := matrix.New(cfg, client) // no media store\n\n// after\n store := media.NewLocalStore(cfg.MediaDir)\n ch := matrix.New(cfg, client, matrix.WithMediaStore(store))","handlingStrategy":"validation","validationCode":"// fail fast at construction/startup, not on first media send\nif matrixCh.GetMediaStore() == nil {\n\treturn errors.New(\"media store not wired into matrix channel; media sending disabled\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := matrixCh.SendMedia(ctx, msg); err != nil {\n\tif errors.Is(err, channels.ErrSendFailed) && strings.Contains(err.Error(), \"no media store\") {\n\t\tlog.Error(\"channel built without media store; fix construction, message dead-lettered\")\n\t\tdeadLetter(msg)\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Wire the media store in the channel builder and assert non-nil at startup","Disable media-producing features when the store is absent instead of failing per message","Cover the media path in channel construction tests so unwired builds fail CI"],"tags":["go","matrix","media","configuration","wiring","validation","permanent-failure"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}