sipeed/picoclaw · error · ErrSendFailed
no media store available: %w
Error message
no media store available: %w
What it means
SendMedia needs a media store to resolve each part.Ref into a readable local file before uploading to Discord; GetMediaStore() returned nil, meaning SetMediaStore was never called on the channel. It wraps channels.ErrSendFailed, so the manager treats it as permanent and will not retry. The message text also carries ErrSendFailed via %w.
Source
Thrown at pkg/channels/discord/discord.go:274
}
return vc, true
}
// SendMedia implements the channels.MediaSender interface.
func (c *DiscordChannel) SendMedia(ctx context.Context, msg bus.OutboundMediaMessage) ([]string, error) {
if !c.IsRunning() {
return nil, channels.ErrNotRunning
}
channelID := msg.ChatID
if channelID == "" {
return nil, fmt.Errorf("channel ID is empty")
}
trackedMsgID, hasTrackedMsg := c.currentToolFeedbackMessage(channelID)
store := c.GetMediaStore()
if store == nil {
return nil, fmt.Errorf("no media store available: %w", channels.ErrSendFailed)
}
// Collect all files into a single ChannelMessageSendComplex call
files := make([]*discordgo.File, 0, len(msg.Parts))
var caption string
for _, part := range msg.Parts {
localPath, err := store.Resolve(part.Ref)
if err != nil {
logger.ErrorCF("discord", "Failed to resolve media ref", map[string]any{
"ref": part.Ref,
"error": err.Error(),
})
continue
}
file, err := os.Open(localPath)
if err != nil {View on GitHub (pinned to 49183d7e8d)
Solutions
- Attach a media store before any media send: ch.SetMediaStore(store) with a media.MediaStore implementation
- If using the standard Manager, ensure it is the one constructing/starting channels so SetMediaStore propagates to existing channels
- Check startup ordering: attach the store before the agent can publish OutboundMediaMessage
Example fix
// before ch, _ := discord.NewDiscordChannel(bc, cfg, bus) _ = ch.Start(ctx) // later SendMedia fails: no media store available // after ch, _ := discord.NewDiscordChannel(bc, cfg, bus) ch.SetMediaStore(store) // media.MediaStore implementation _ = ch.Start(ctx)
Defensive patterns
Strategy: validation
Validate before calling
if ch.GetMediaStore() == nil {
return fmt.Errorf("discord channel has no media store; attach one before sending media")
} Type guard
func hasMediaStore(ch interface{ GetMediaStore() media.MediaStore }) bool {
return ch.GetMediaStore() != nil
} Try / catch
if _, err := ch.SendMedia(ctx, msg); err != nil {
if errors.Is(err, channels.ErrSendFailed) && strings.Contains(err.Error(), "no media store") {
// permanent: wire the store, do not retry
ch.SetMediaStore(store)
return ch.SendMedia(ctx, msg)
}
return err
} Prevention
- Construct channels through the Manager so SetMediaStore propagates automatically
- Attach the media store before Start / before the agent can emit media
- Smoke-test SendMedia once at startup to fail fast on missing wiring
When it happens
Trigger: Constructing and starting the Discord channel without attaching a media store — e.g. embedding the channel outside the Manager that normally calls Manager.SetMediaStore (manager.go), or a startup ordering where media arrives before the store is attached.
Common situations: Custom embeddings of PicoClaw channels that skip manager wiring, tests exercising SendMedia without a store (see qq_test's equivalent), disabling the media subsystem in config while the agent still emits images/files.
Related errors
- media store not configured
- discord send media: %w
- no media store available: %w
- no media store available: %w
- decrypt media: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/343a97acc5f73574.
Report an issue: GitHub.