sipeed/picoclaw · warning
no target channel/chat available
Error message
no target channel/chat available
What it means
Returned by tts.SynthesizeAndStore when channel or chatID is empty. These identify where the audio will be registered — they feed the media store scope 'tool:send_tts:<channel>:<chatID>:<timestamp>' — so without them the store cannot namespace the media.
Source
Thrown at pkg/audio/tts/tts.go:105
// SynthesizeAndStore synthesizes text to speech and registers it in the media store, returning the media reference.
func SynthesizeAndStore(
ctx context.Context,
provider TTSProvider,
store media.MediaStore,
text string,
filename string,
channel string,
chatID string,
) (string, error) {
if provider == nil {
return "", fmt.Errorf("tts provider is not configured")
}
if store == nil {
return "", fmt.Errorf("media store not configured")
}
if channel == "" || chatID == "" {
return "", fmt.Errorf("no target channel/chat available")
}
if strings.TrimSpace(text) == "" {
return "", fmt.Errorf("text is required")
}
stream, err := provider.Synthesize(ctx, text)
if err != nil {
return "", fmt.Errorf("tts synthesize failed: %w", err)
}
defer stream.Close()
err = os.MkdirAll(media.TempDir(), 0o700)
if err != nil {
return "", fmt.Errorf("failed to create media temp dir: %w", err)
}
fileExt := ".ogg"
contentType := "audio/ogg"View on GitHub (pinned to 49183d7e8d)
Solutions
- Derive channel/chatID from the originating message before calling SynthesizeAndStore
- Default them to explicit values (e.g. 'console'/'local') for non-chat contexts
- Skip voice output when the calling context has no target
Example fix
// before
ref, err := tts.SynthesizeAndStore(ctx, p, s, text, name, ``, ``)
// after
if channel == `` {
channel = `console`
}
if chatID == `` {
chatID = `local`
}
ref, err := tts.SynthesizeAndStore(ctx, p, s, text, name, channel, chatID) Defensive patterns
Strategy: validation
Validate before calling
if channel == `` || chatID == `` {
return errors.New(`tts requires a target channel and chat id; derive them from the originating message`)
} Type guard
func isNoTargetError(err error) bool {
return err != nil && strings.Contains(err.Error(), `no target channel/chat`)
} Try / catch
if err != nil && isNoTargetError(err) {
// context has no chat routing: skip voice output or supply explicit defaults
} Prevention
- Thread channel/chatID from the originating message into every TTS call
- Give non-chat contexts (cron, CLI) explicit default identifiers
- Validate routing fields before entering the voice pipeline
When it happens
Trigger: Invoking TTS from a context without chat routing: scheduled jobs, CLI commands, webhooks that carry no channel; passing zero-value strings from a message struct that was never populated.
Common situations: New integrations (cron announcements, test harnesses) that bypass the chat message path.
Related errors
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/ad9bd3af9fe974cd.
Report an issue: GitHub.