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

  1. Derive channel/chatID from the originating message before calling SynthesizeAndStore
  2. Default them to explicit values (e.g. 'console'/'local') for non-chat contexts
  3. 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

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.